From 99b858b2a28eaabe093e61b58f78dcf30e2c16e3 Mon Sep 17 00:00:00 2001 From: Maksymilian Wojczuk Date: Wed, 21 Mar 2018 14:58:58 +0100 Subject: [PATCH 1/2] mfa configuration #91 Fixed default behaviour for mfa configuration --- configuration/configuration.go | 4 +- mysession/mysession.go | 148 ++++++++++++++++++--------------- stack/stack.go | 35 ++++---- 3 files changed, 102 insertions(+), 85 deletions(-) diff --git a/configuration/configuration.go b/configuration/configuration.go index 8f6751d..526ae11 100644 --- a/configuration/configuration.go +++ b/configuration/configuration.go @@ -97,8 +97,10 @@ func postProcessing(config *Configuration, cliArguments cliparser.CliArguments) if *cliArguments.Profile != "" { config.DefaultProfile = *cliArguments.Profile } - if *cliArguments.MFA != config.DefaultDecisionForMFA { + if *cliArguments.MFA { config.DefaultDecisionForMFA = *cliArguments.MFA + } else { + *cliArguments.MFA = config.DefaultDecisionForMFA } if *cliArguments.DurationForMFA > 0 { config.DefaultDurationForMFA = *cliArguments.DurationForMFA diff --git a/mysession/mysession.go b/mysession/mysession.go index 3032763..186a2a4 100644 --- a/mysession/mysession.go +++ b/mysession/mysession.go @@ -8,12 +8,27 @@ import ( "github.com/aws/aws-sdk-go/aws/session" "github.com/aws/aws-sdk-go/service/sts" "github.com/go-ini/ini" + "os" "os/user" "time" ) const dateFormat = "2006-01-02 15:04:05 MST" +func InitializeSession(context *context.Context) *session.Session { + tokenError := UpdateSessionToken(context.Config.DefaultProfile, context.Config.DefaultRegion, context.Config.DefaultDurationForMFA, context) + if tokenError != nil { + context.Logger.Error(tokenError.Error()) + os.Exit(1) + } + session, sessionError := CreateSession(context, context.Config.DefaultProfile, &context.Config.DefaultRegion) + if sessionError != nil { + context.Logger.Error(sessionError.Error()) + os.Exit(1) + } + return session +} + func CreateSession(context *context.Context, profile string, region *string) (*session.Session, error) { context.Logger.Info("Profile: " + profile) context.Logger.Info("Region: " + *region) @@ -34,89 +49,90 @@ func CreateSession(context *context.Context, profile string, region *string) (*s } func UpdateSessionToken(profile string, region string, defaultDuration int64, context *context.Context) error { - user, userError := user.Current() - if userError != nil { - return userError - } + if *context.CliArguments.MFA { + user, userError := user.Current() + if userError != nil { + return userError + } - credentialsFilePath := user.HomeDir + "/.aws/credentials" - configuration, loadCredentialsError := ini.Load(credentialsFilePath) - if loadCredentialsError != nil { - return loadCredentialsError - } + credentialsFilePath := user.HomeDir + "/.aws/credentials" + configuration, loadCredentialsError := ini.Load(credentialsFilePath) + if loadCredentialsError != nil { + return loadCredentialsError + } - section, sectionError := configuration.GetSection(profile) - if sectionError != nil { - section, sectionError = configuration.NewSection(profile) + section, sectionError := configuration.GetSection(profile) if sectionError != nil { - return sectionError + section, sectionError = configuration.NewSection(profile) + if sectionError != nil { + return sectionError + } } - } - - profileLongTerm := profile + "-long-term" - sectionLongTerm, profileLongTermError := configuration.GetSection(profileLongTerm) - if profileLongTermError != nil { - return profileLongTermError - } - sessionToken := section.Key("aws_session_token") - expiration := section.Key("expiration") - - expirationDate, dataError := time.Parse(dateFormat, section.Key("expiration").Value()) - if dataError == nil { - context.Logger.Info("Session token will expire in " + utilities.TruncateDuration(time.Since(expirationDate)).String() + " (" + expirationDate.Format(dateFormat) + ")") - } + profileLongTerm := profile + "-long-term" + sectionLongTerm, profileLongTermError := configuration.GetSection(profileLongTerm) + if profileLongTermError != nil { + return profileLongTermError + } - mfaDevice := sectionLongTerm.Key("mfa_serial").Value() - if mfaDevice == "" { - return errors.New("There is no mfa_serial for the profile " + profileLongTerm) - } + sessionToken := section.Key("aws_session_token") + expiration := section.Key("expiration") - if sessionToken.Value() == "" || expiration.Value() == "" || time.Since(expirationDate).Nanoseconds() > 0 { - session, sessionError := session.NewSessionWithOptions( - session.Options{ - Config: aws.Config{ - Region: ®ion, - }, - Profile: profileLongTerm, - }) - if sessionError != nil { - return sessionError + expirationDate, dataError := time.Parse(dateFormat, section.Key("expiration").Value()) + if dataError == nil { + context.Logger.Info("Session token will expire in " + utilities.TruncateDuration(time.Since(expirationDate)).String() + " (" + expirationDate.Format(dateFormat) + ")") } - var tokenCode string - sessionError = context.Logger.GetInput("MFA token code", &tokenCode) - if sessionError != nil { - return sessionError + mfaDevice := sectionLongTerm.Key("mfa_serial").Value() + if mfaDevice == "" { + return errors.New("There is no mfa_serial for the profile " + profileLongTerm + ". If you haven't used --mfa option you can change the default decision for MFA in the configuration file") } - var duration int64 - if defaultDuration == 0 { - sessionError = context.Logger.GetInput("Duration", &duration) + if sessionToken.Value() == "" || expiration.Value() == "" || time.Since(expirationDate).Nanoseconds() > 0 { + session, sessionError := session.NewSessionWithOptions( + session.Options{ + Config: aws.Config{ + Region: ®ion, + }, + Profile: profileLongTerm, + }) if sessionError != nil { return sessionError } - } else { - duration = defaultDuration - } - stsSession := sts.New(session) - newToken, tokenError := stsSession.GetSessionToken(&sts.GetSessionTokenInput{ - DurationSeconds: &duration, - SerialNumber: aws.String(mfaDevice), - TokenCode: &tokenCode, - }) - if tokenError != nil { - return tokenError - } + var tokenCode string + sessionError = context.Logger.GetInput("MFA token code", &tokenCode) + if sessionError != nil { + return sessionError + } - section.Key("aws_access_key_id").SetValue(*newToken.Credentials.AccessKeyId) - section.Key("aws_secret_access_key").SetValue(*newToken.Credentials.SecretAccessKey) - sessionToken.SetValue(*newToken.Credentials.SessionToken) - section.Key("expiration").SetValue(newToken.Credentials.Expiration.Format(dateFormat)) + var duration int64 + if defaultDuration == 0 { + sessionError = context.Logger.GetInput("Duration", &duration) + if sessionError != nil { + return sessionError + } + } else { + duration = defaultDuration + } - configuration.SaveTo(credentialsFilePath) - } + stsSession := sts.New(session) + newToken, tokenError := stsSession.GetSessionToken(&sts.GetSessionTokenInput{ + DurationSeconds: &duration, + SerialNumber: aws.String(mfaDevice), + TokenCode: &tokenCode, + }) + if tokenError != nil { + return tokenError + } + section.Key("aws_access_key_id").SetValue(*newToken.Credentials.AccessKeyId) + section.Key("aws_secret_access_key").SetValue(*newToken.Credentials.SecretAccessKey) + sessionToken.SetValue(*newToken.Credentials.SessionToken) + section.Key("expiration").SetValue(newToken.Credentials.Expiration.Format(dateFormat)) + + configuration.SaveTo(credentialsFilePath) + } + } return nil } diff --git a/stack/stack.go b/stack/stack.go index 0f49f2c..f1f5d5a 100644 --- a/stack/stack.go +++ b/stack/stack.go @@ -6,10 +6,11 @@ import ( "github.com/aws/aws-sdk-go/aws/session" "github.com/aws/aws-sdk-go/service/cloudformation" "io/ioutil" + "os" ) // This function gets template and name of stack. It creates "CreateStackInput" structure. -func createStackInput(context *context.Context, template *string, stackName *string) cloudformation.CreateStackInput { +func createStackInput(template *string, stackName *string) cloudformation.CreateStackInput { templateStruct := cloudformation.CreateStackInput{ TemplateBody: template, StackName: stackName, @@ -32,35 +33,33 @@ func getTemplateFromFile(context *context.Context) (string, string) { } // This function uses CreateStackInput variable to create Stack. -func createStack(templateStruct cloudformation.CreateStackInput, session *session.Session) { +func createStack(templateStruct cloudformation.CreateStackInput, session *session.Session, context *context.Context) { api := cloudformation.New(session) - api.CreateStack(&templateStruct) + _, err := api.CreateStack(&templateStruct) + if err != nil { + context.Logger.Error(err.Error()) + os.Exit(1) + } } // This function uses all functions above and session to create Stack. func NewStack(context *context.Context) { template, stackName := getTemplateFromFile(context) - templateStruct := createStackInput(context, &template, &stackName) - tokenError := mysession.UpdateSessionToken(context.Config.DefaultProfile, context.Config.DefaultRegion, context.Config.DefaultDurationForMFA, context) - if tokenError != nil { - context.Logger.Error(tokenError.Error()) - } - session, createSessionError := mysession.CreateSession(context, context.Config.DefaultProfile, &context.Config.DefaultRegion) - if createSessionError != nil { - context.Logger.Error(createSessionError.Error()) - } - createStack(templateStruct, session) + templateStruct := createStackInput(&template, &stackName) + session := mysession.InitializeSession(context) + createStack(templateStruct, session, context) } // This function bases on "DeleteStackInput" structure and destroys stack. It uses "StackName" to choose which stack will be destroy. Before that it creates session. func DestroyStack(context *context.Context) { delStackInput := deleteStackInput(context) - session, sessionError := mysession.CreateSession(context, context.Config.DefaultProfile, &context.Config.DefaultRegion) - if sessionError != nil { - context.Logger.Error(sessionError.Error()) - } + session := mysession.InitializeSession(context) api := cloudformation.New(session) - api.DeleteStack(&delStackInput) + _, err := api.DeleteStack(&delStackInput) + if err != nil { + context.Logger.Error(err.Error()) + os.Exit(1) + } } // This function gets "StackName" from Stack in CliArguments and creates "DeleteStackInput" structure. From 191727bac9821baab7a96c2774bafe4be534da30 Mon Sep 17 00:00:00 2001 From: Maksymilian Wojczuk Date: Mon, 18 Jun 2018 10:12:45 +0200 Subject: [PATCH 2/2] MFA Configuration Updated mysession for mfa configuration possibility --- mysession/mysession.go | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/mysession/mysession.go b/mysession/mysession.go index 186a2a4..0327f61 100644 --- a/mysession/mysession.go +++ b/mysession/mysession.go @@ -2,6 +2,7 @@ package mysession import ( "errors" + "github.com/Appliscale/perun/cliparser" "github.com/Appliscale/perun/context" "github.com/Appliscale/perun/utilities" "github.com/aws/aws-sdk-go/aws" @@ -49,7 +50,7 @@ func CreateSession(context *context.Context, profile string, region *string) (*s } func UpdateSessionToken(profile string, region string, defaultDuration int64, context *context.Context) error { - if *context.CliArguments.MFA { + if *context.CliArguments.MFA || *context.CliArguments.Mode == cliparser.MfaMode { user, userError := user.Current() if userError != nil { return userError