-
Notifications
You must be signed in to change notification settings - Fork 0
/
main.go
172 lines (145 loc) · 4.34 KB
/
main.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
package main
import (
"encoding/json"
"flag"
"fmt"
"net/url"
"github.com/aws/aws-sdk-go/aws"
"github.com/aws/aws-sdk-go/aws/awserr"
"github.com/aws/aws-sdk-go/aws/session"
"github.com/aws/aws-sdk-go/service/iam"
"github.com/pkg/errors"
)
type policyDocument struct {
Version string `json:"Version"`
Statement []policyStatement `json:"Statement"`
}
func (pd *policyDocument) AddStatement(statement policyStatement) error {
found := false
for _, element := range pd.Statement {
if element.Principal.AWS == statement.Principal.AWS {
return fmt.Errorf("arn (%s) already trusted by role (%s)", awsARN, roleName)
}
}
if !found {
pd.Statement = append(pd.Statement, statement)
}
return nil
}
type policyStatement struct {
Action string `json:"Action"`
Effect string `json:"Effect"`
Principal policyPrincipal `json:"Principal"`
}
type policyPrincipal struct {
Service string `json:"Service,omitempty"`
AWS string `json:"AWS,omitempty"`
}
var (
awsARN string
roleName string
)
func main() {
flag.StringVar(&awsARN, "arn", "", "ARN Being Added")
flag.StringVar(&roleName, "role-name", "", "Role Name To Edit")
flag.Parse()
if awsARN == "" || roleName == "" {
fmt.Println("-arn and -role-name are required.")
return
}
// create the service
svc := iam.New(session.New())
// fetch the document
document, err := getExistingPolicyDocument(svc)
if err != nil {
panic(err.Error())
}
// add the arn to the document
err = addARNToDocument(document)
if err != nil {
panic(err.Error())
}
// update the document this will only be hit if the arn doesn't exist in the current document
err = updatePolicyDocument(svc, document)
if err != nil {
panic(err.Error())
}
}
func addARNToDocument(document policyDocument) error {
// try and add it to the role
err := document.AddStatement(
policyStatement{
Effect: "Allow",
Action: "sts:AssumeRole",
Principal: policyPrincipal{
AWS: awsARN,
},
},
)
return errors.Wrap(err, "Adding ARN to document")
}
func updatePolicyDocument(svc *iam.IAM, document policyDocument) error {
// convert the document back into json
updatedDocument, err := json.Marshal(document)
if err != nil {
return errors.Wrap(err, "Converting Document into JSON")
}
// create the update assume role policy input
input1 := &iam.UpdateAssumeRolePolicyInput{
PolicyDocument: aws.String(string(updatedDocument)),
RoleName: aws.String(roleName),
}
// update the role
_, err = svc.UpdateAssumeRolePolicy(input1)
if err != nil {
if aerr, ok := err.(awserr.Error); ok {
switch aerr.Code() {
case iam.ErrCodeNoSuchEntityException:
return errors.Wrap(aerr, iam.ErrCodeNoSuchEntityException)
case iam.ErrCodeMalformedPolicyDocumentException:
return errors.Wrap(aerr, iam.ErrCodeMalformedPolicyDocumentException)
case iam.ErrCodeLimitExceededException:
return errors.Wrap(aerr, iam.ErrCodeLimitExceededException)
case iam.ErrCodeUnmodifiableEntityException:
return errors.Wrap(aerr, iam.ErrCodeUnmodifiableEntityException)
case iam.ErrCodeServiceFailureException:
return errors.Wrap(aerr, iam.ErrCodeServiceFailureException)
default:
errors.Wrap(aerr, "Unknown AWS Error")
}
}
}
return nil
}
func getExistingPolicyDocument(svc *iam.IAM) (policyDocument, error) {
var document policyDocument
input := &iam.GetRoleInput{
RoleName: aws.String(roleName),
}
result, err := svc.GetRole(input)
if err != nil {
if aerr, ok := err.(awserr.Error); ok {
switch aerr.Code() {
case iam.ErrCodeNoSuchEntityException:
return document, errors.Wrap(aerr, iam.ErrCodeNoSuchEntityException)
case iam.ErrCodeServiceFailureException:
return document, errors.Wrap(aerr, iam.ErrCodeServiceFailureException)
default:
return document, errors.Wrap(aerr, "Unknown AWS Error")
}
}
// Print the error, cast err to awserr.Error to get the Code and
// Message from an error.
return document, errors.Wrap(err, "Generic Error")
}
// get the role policy document from the result
rawValue := aws.StringValue(result.Role.AssumeRolePolicyDocument)
// clean it up
str, err := url.QueryUnescape(rawValue)
if err != nil {
return document, errors.Wrap(err, "Query Unescape Error")
}
// create the golang version of it
err = json.Unmarshal([]byte(str), &document)
return document, errors.Wrap(err, "Converting Json to Struct")
}