github.com/in4it/ecs-deploy@v0.0.42-0.20240508120354-ed77ff16df25/provider/ecs/iam.go (about)

     1  package ecs
     2  
     3  import (
     4  	"github.com/aws/aws-sdk-go/aws"
     5  	"github.com/aws/aws-sdk-go/aws/awserr"
     6  	"github.com/aws/aws-sdk-go/aws/credentials"
     7  	"github.com/aws/aws-sdk-go/aws/credentials/stscreds"
     8  	"github.com/aws/aws-sdk-go/aws/session"
     9  	"github.com/aws/aws-sdk-go/service/iam"
    10  	"github.com/aws/aws-sdk-go/service/sts"
    11  	"github.com/juju/loggo"
    12  
    13  	"encoding/json"
    14  	"errors"
    15  	"fmt"
    16  )
    17  
    18  // logging
    19  var iamLogger = loggo.GetLogger("iam")
    20  
    21  // IAM struct
    22  type IAM struct {
    23  	stsAssumingRole *sts.STS
    24  	AccountId       string
    25  }
    26  
    27  // default IAM trust
    28  
    29  func (e *IAM) GetEcsTaskIAMTrust() string {
    30  	return `{ "Version": "2012-10-17", "Statement": [ { "Action": "sts:AssumeRole", "Principal": { "Service": "ecs-tasks.amazonaws.com" }, "Effect": "Allow" } ] }`
    31  }
    32  func (e *IAM) GetEcsServiceIAMTrust() string {
    33  	return `{ "Version": "2012-10-17", "Statement": [ { "Action": "sts:AssumeRole", "Principal": { "Service": "ecs.amazonaws.com" }, "Effect": "Allow" } ] }`
    34  }
    35  func (e *IAM) GetEC2IAMTrust() string {
    36  	return `{ "Version": "2012-10-17", "Statement": [ { "Action": "sts:AssumeRole", "Principal": { "Service": "ec2.amazonaws.com" }, "Effect": "Allow" } ] }`
    37  }
    38  func (e *IAM) GetEcsAppAutoscalingIAMTrust() string {
    39  	return `{ "Version": "2012-10-17", "Statement": [ { "Action": "sts:AssumeRole", "Principal": { "Service": "application-autoscaling.amazonaws.com" }, "Effect": "Allow" } ] }`
    40  }
    41  func (e *IAM) GetEcsServicePolicy() string {
    42  	return `arn:aws:iam::aws:policy/service-role/AmazonEC2ContainerServiceRole`
    43  }
    44  
    45  func (e *IAM) GetAccountId() error {
    46  	var svc *sts.STS
    47  	if e.stsAssumingRole == nil {
    48  		svc = sts.New(session.New())
    49  	} else {
    50  		svc = e.stsAssumingRole
    51  	}
    52  	input := &sts.GetCallerIdentityInput{}
    53  
    54  	result, err := svc.GetCallerIdentity(input)
    55  	if err != nil {
    56  		if aerr, ok := err.(awserr.Error); ok {
    57  			switch aerr.Code() {
    58  			default:
    59  				iamLogger.Errorf(aerr.Error())
    60  			}
    61  		} else {
    62  			iamLogger.Errorf(err.Error())
    63  		}
    64  		return errors.New("Couldn't get caller identity")
    65  	}
    66  	e.AccountId = *result.Account
    67  	return nil
    68  }
    69  
    70  func (e *IAM) RoleExists(roleName string) (*string, error) {
    71  	svc := iam.New(session.New())
    72  	input := &iam.GetRoleInput{
    73  		RoleName: aws.String(roleName),
    74  	}
    75  
    76  	result, err := svc.GetRole(input)
    77  	if err != nil {
    78  		if aerr, ok := err.(awserr.Error); ok {
    79  			switch aerr.Code() {
    80  			case iam.ErrCodeNoSuchEntityException:
    81  				return nil, nil
    82  			case iam.ErrCodeServiceFailureException:
    83  				iamLogger.Errorf(iam.ErrCodeServiceFailureException+": %v", aerr.Error())
    84  			default:
    85  				iamLogger.Errorf(aerr.Error())
    86  			}
    87  		} else {
    88  			iamLogger.Errorf(err.Error())
    89  		}
    90  		return nil, errors.New(fmt.Sprintf("Could not retrieve role: %v (check AWS credentials)", roleName))
    91  	}
    92  	return result.Role.Arn, nil
    93  }
    94  
    95  func (e *IAM) CreateRoleWithPermissionBoundary(roleName, assumePolicyDocument, permissionBoundaryARN string) (*string, error) {
    96  	svc := iam.New(session.New())
    97  	input := &iam.CreateRoleInput{
    98  		AssumeRolePolicyDocument: aws.String(assumePolicyDocument),
    99  		Path:                     aws.String("/"),
   100  		RoleName:                 aws.String(roleName),
   101  	}
   102  
   103  	if permissionBoundaryARN != "" {
   104  		input.SetPermissionsBoundary(permissionBoundaryARN)
   105  	}
   106  
   107  	result, err := svc.CreateRole(input)
   108  	if err != nil {
   109  		iamLogger.Errorf(err.Error())
   110  		return nil, errors.New(fmt.Sprintf("Could not create role: %v", roleName))
   111  	} else {
   112  		return result.Role.Arn, nil
   113  	}
   114  }
   115  
   116  func (e *IAM) CreateRole(roleName, assumePolicyDocument string) (*string, error) {
   117  	return e.CreateRoleWithPermissionBoundary(roleName, assumePolicyDocument, "")
   118  }
   119  func (e *IAM) DeleteRolePolicy(roleName, policyName string) error {
   120  	svc := iam.New(session.New())
   121  	input := &iam.DeleteRolePolicyInput{
   122  		RoleName:   aws.String(roleName),
   123  		PolicyName: aws.String(policyName),
   124  	}
   125  
   126  	_, err := svc.DeleteRolePolicy(input)
   127  	if err != nil {
   128  		if aerr, ok := err.(awserr.Error); ok {
   129  			iamLogger.Errorf(aerr.Error())
   130  		} else {
   131  			iamLogger.Errorf(err.Error())
   132  		}
   133  		return err
   134  	}
   135  	return nil
   136  }
   137  func (e *IAM) DeleteRole(roleName string) error {
   138  	svc := iam.New(session.New())
   139  	input := &iam.DeleteRoleInput{
   140  		RoleName: aws.String(roleName),
   141  	}
   142  
   143  	_, err := svc.DeleteRole(input)
   144  	if err != nil {
   145  		if aerr, ok := err.(awserr.Error); ok {
   146  			iamLogger.Errorf(aerr.Error())
   147  		} else {
   148  			iamLogger.Errorf(err.Error())
   149  		}
   150  		return err
   151  	}
   152  	return nil
   153  }
   154  func (e *IAM) CreateInstanceProfile(instanceProfileName string) error {
   155  	svc := iam.New(session.New())
   156  	input := &iam.CreateInstanceProfileInput{
   157  		InstanceProfileName: aws.String(instanceProfileName),
   158  		Path:                aws.String("/"),
   159  	}
   160  
   161  	_, err := svc.CreateInstanceProfile(input)
   162  	if err != nil {
   163  		if aerr, ok := err.(awserr.Error); ok {
   164  			iamLogger.Errorf(aerr.Error())
   165  		} else {
   166  			iamLogger.Errorf(err.Error())
   167  		}
   168  		return err
   169  	}
   170  	return nil
   171  }
   172  func (e *IAM) AddRoleToInstanceProfile(instanceProfileName, roleName string) error {
   173  	svc := iam.New(session.New())
   174  	input := &iam.AddRoleToInstanceProfileInput{
   175  		InstanceProfileName: aws.String(instanceProfileName),
   176  		RoleName:            aws.String(roleName),
   177  	}
   178  
   179  	_, err := svc.AddRoleToInstanceProfile(input)
   180  	if err != nil {
   181  		if aerr, ok := err.(awserr.Error); ok {
   182  			iamLogger.Errorf(aerr.Error())
   183  		} else {
   184  			iamLogger.Errorf(err.Error())
   185  		}
   186  		return err
   187  	}
   188  	return nil
   189  }
   190  func (e *IAM) RemoveRoleFromInstanceProfile(instanceProfileName, roleName string) error {
   191  	svc := iam.New(session.New())
   192  	input := &iam.RemoveRoleFromInstanceProfileInput{
   193  		InstanceProfileName: aws.String(instanceProfileName),
   194  		RoleName:            aws.String(roleName),
   195  	}
   196  
   197  	_, err := svc.RemoveRoleFromInstanceProfile(input)
   198  	if err != nil {
   199  		if aerr, ok := err.(awserr.Error); ok {
   200  			iamLogger.Errorf(aerr.Error())
   201  		} else {
   202  			iamLogger.Errorf(err.Error())
   203  		}
   204  		return err
   205  	}
   206  	return nil
   207  }
   208  func (e *IAM) DeleteInstanceProfile(instanceProfileName string) error {
   209  	svc := iam.New(session.New())
   210  	input := &iam.DeleteInstanceProfileInput{
   211  		InstanceProfileName: aws.String(instanceProfileName),
   212  	}
   213  
   214  	_, err := svc.DeleteInstanceProfile(input)
   215  	if err != nil {
   216  		if aerr, ok := err.(awserr.Error); ok {
   217  			iamLogger.Errorf(aerr.Error())
   218  		} else {
   219  			iamLogger.Errorf(err.Error())
   220  		}
   221  		return err
   222  	}
   223  	return nil
   224  }
   225  func (e *IAM) WaitUntilInstanceProfileExists(instanceProfileName string) error {
   226  	svc := iam.New(session.New())
   227  	input := &iam.GetInstanceProfileInput{
   228  		InstanceProfileName: aws.String(instanceProfileName),
   229  	}
   230  
   231  	err := svc.WaitUntilInstanceProfileExists(input)
   232  	if err != nil {
   233  		if aerr, ok := err.(awserr.Error); ok {
   234  			iamLogger.Errorf(aerr.Error())
   235  		} else {
   236  			iamLogger.Errorf(err.Error())
   237  		}
   238  		return err
   239  	}
   240  	return nil
   241  }
   242  
   243  func (e *IAM) PutRolePolicy(roleName, policyName, policy string) error {
   244  	svc := iam.New(session.New())
   245  
   246  	input := &iam.PutRolePolicyInput{
   247  		PolicyDocument: aws.String(policy),
   248  		PolicyName:     aws.String(policyName),
   249  		RoleName:       aws.String(roleName),
   250  	}
   251  
   252  	_, err := svc.PutRolePolicy(input)
   253  	if err != nil {
   254  		if aerr, ok := err.(awserr.Error); ok {
   255  			switch aerr.Code() {
   256  			case iam.ErrCodeLimitExceededException:
   257  				iamLogger.Errorf(iam.ErrCodeLimitExceededException+": %v", aerr.Error())
   258  			case iam.ErrCodeMalformedPolicyDocumentException:
   259  				iamLogger.Errorf(iam.ErrCodeMalformedPolicyDocumentException+": %v", aerr.Error())
   260  			case iam.ErrCodeNoSuchEntityException:
   261  				iamLogger.Errorf(iam.ErrCodeNoSuchEntityException+": %v", aerr.Error())
   262  			case iam.ErrCodeUnmodifiableEntityException:
   263  				iamLogger.Errorf(iam.ErrCodeUnmodifiableEntityException+": %v", aerr.Error())
   264  			case iam.ErrCodeServiceFailureException:
   265  				iamLogger.Errorf(iam.ErrCodeServiceFailureException+": %v", aerr.Error())
   266  			default:
   267  				iamLogger.Errorf(aerr.Error())
   268  			}
   269  		} else {
   270  			iamLogger.Errorf(err.Error())
   271  		}
   272  		return errors.New(fmt.Sprintf("Could not put role policy for: %v", roleName))
   273  	}
   274  	return nil
   275  }
   276  func (e *IAM) AttachRolePolicy(roleName, policyArn string) error {
   277  	svc := iam.New(session.New())
   278  	input := &iam.AttachRolePolicyInput{
   279  		PolicyArn: aws.String(policyArn),
   280  		RoleName:  aws.String(roleName),
   281  	}
   282  
   283  	_, err := svc.AttachRolePolicy(input)
   284  	if err != nil {
   285  		if aerr, ok := err.(awserr.Error); ok {
   286  			switch aerr.Code() {
   287  			case iam.ErrCodeNoSuchEntityException:
   288  				iamLogger.Errorf(iam.ErrCodeNoSuchEntityException, aerr.Error())
   289  			case iam.ErrCodeLimitExceededException:
   290  				iamLogger.Errorf(iam.ErrCodeLimitExceededException, aerr.Error())
   291  			case iam.ErrCodeInvalidInputException:
   292  				iamLogger.Errorf(iam.ErrCodeInvalidInputException, aerr.Error())
   293  			case iam.ErrCodeUnmodifiableEntityException:
   294  				iamLogger.Errorf(iam.ErrCodeUnmodifiableEntityException, aerr.Error())
   295  			case iam.ErrCodePolicyNotAttachableException:
   296  				iamLogger.Errorf(iam.ErrCodePolicyNotAttachableException, aerr.Error())
   297  			case iam.ErrCodeServiceFailureException:
   298  				iamLogger.Errorf(iam.ErrCodeServiceFailureException, aerr.Error())
   299  			default:
   300  				iamLogger.Errorf(aerr.Error())
   301  			}
   302  		} else {
   303  			// Print the error, cast err to awserr.Error to get the Code and
   304  			// Message from an error.
   305  			iamLogger.Errorf(err.Error())
   306  		}
   307  		return errors.New("Could not attach role policy to role")
   308  	}
   309  	return nil
   310  }
   311  
   312  func (e *IAM) AssumeRole(roleArn, roleSessionName, prevCreds string) (*credentials.Credentials, string, error) {
   313  	sess := session.Must(session.NewSession())
   314  	// check previous credentials
   315  	var value credentials.Value
   316  	var creds *credentials.Credentials
   317  	if prevCreds != "" {
   318  		iamLogger.Debugf("Found previous credentials")
   319  		err := json.Unmarshal([]byte(prevCreds), &value)
   320  		if err == nil {
   321  			iamLogger.Debugf("Unmarshalled previous credentials")
   322  			creds = credentials.NewStaticCredentialsFromCreds(value)
   323  			// test old credentials
   324  			e.stsAssumingRole = sts.New(sess, &aws.Config{Credentials: creds})
   325  			if e.stsAssumingRole == nil {
   326  				return creds, "", errors.New("Could not assume role")
   327  			}
   328  			err = e.GetAccountId()
   329  			if err != nil {
   330  				iamLogger.Debugf("Credentials are expired")
   331  				creds = nil
   332  			}
   333  		}
   334  	}
   335  	// retrieve new credentials for roleArn
   336  	if creds == nil {
   337  		iamLogger.Debugf("Using new credentials")
   338  		creds = stscreds.NewCredentials(sess, roleArn, func(a *stscreds.AssumeRoleProvider) {
   339  			a.RoleSessionName = roleSessionName
   340  		})
   341  	}
   342  	// convert credentials to json
   343  	valCreds, err := creds.Get()
   344  	if err != nil {
   345  		return creds, "", err
   346  	}
   347  	jsonCreds, err := json.Marshal(valCreds)
   348  	if err != nil {
   349  		return creds, "", err
   350  	}
   351  	return creds, string(jsonCreds), nil
   352  }