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 }