-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathmain.go
187 lines (159 loc) · 5.18 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
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
// Package roles does unauthenticated enumeration of IAM role ARNs. The goal is to find all variations of specific roles
// in a given account rather than across a list of roles in all accounts like quiet riot does. Some role roles include
// random prefixes so we want to guess those as well if possible.
//
// The roles we are looking for may include placeholders like {accountId} or {region} which we may already know.
package main
import (
"context"
_ "embed"
"flag"
"fmt"
"github.com/aws/aws-sdk-go-v2/aws"
"github.com/aws/aws-sdk-go-v2/config"
"github.com/aws/aws-sdk-go-v2/service/sts"
"github.com/ryanjarv/roles/pkg/arn"
"github.com/ryanjarv/roles/pkg/plugins"
"github.com/ryanjarv/roles/pkg/scanner"
"github.com/ryanjarv/roles/pkg/utils"
"github.com/samber/lo"
"os"
"strings"
)
//go:embed data/regions.list
var regionsList string
type Opts struct {
debug bool
setup bool
profile string
name string
rolesPath string
accountsPath string
accountsStr string
concurrency int
force bool
clean bool
}
func main() {
opts := Opts{}
flag.BoolVar(&opts.debug, "debug", false, "Enable debug logging")
flag.BoolVar(&opts.clean, "clean", false, "Cleanup")
flag.StringVar(&opts.profile, "profile", "", "AWS profile to use for scanning")
flag.StringVar(&opts.name, "name", "default", "Name of the scan")
flag.StringVar(&opts.rolesPath, "roles", "", "Additional role names")
flag.StringVar(&opts.accountsPath, "account-list", "", "Path to a file containing account IDs")
flag.StringVar(&opts.accountsStr, "accounts", "", "Path to a file containing account IDs")
flag.IntVar(&opts.concurrency, "concurrency", 2, "Scanner concurrency")
flag.BoolVar(&opts.force, "force", false, "Force rescan")
flag.BoolVar(&opts.setup, "setup", false, "Run optional one-time account optimization setup")
flag.Parse()
ctx := utils.NewContext(context.Background())
if opts.debug {
ctx.Debug.SetOutput(os.Stderr)
}
if opts.setup && opts.clean {
ctx.Error.Fatalf("cannot use both --setup and --clean")
} else if opts.setup {
// Run optional one-time account optimizer
if err := Setup(ctx, opts); err != nil {
ctx.Error.Fatalf("running: %s", err)
}
} else if opts.clean {
if err := CleanUp(ctx, opts); err != nil {
ctx.Error.Fatalf("running: %s", err)
}
} else {
if err := Run(ctx, opts); err != nil {
ctx.Error.Fatalf("running: %s", err)
}
}
}
// LoadAllPlugins loads all enabled plugins.
//
// Add new plugins here.
func LoadAllPlugins(cfgs map[string]aws.Config, caller *sts.GetCallerIdentityOutput) [][]plugins.Plugin {
return [][]plugins.Plugin{
plugins.NewAccessPoints(cfgs, 4, plugins.NewAccessPointInput{
AccountId: *caller.Account,
}),
plugins.NewS3Buckets(cfgs, 4, plugins.NewS3BucketInput{
AccountId: *caller.Account,
}),
plugins.NewSNSTopics(cfgs, 5, plugins.NewSNSInput{
AccountId: *caller.Account,
}),
plugins.NewSQSQueues(cfgs, 5, plugins.NewSQSInput{
AccountId: *caller.Account,
}),
}
}
func Run(ctx *utils.Context, opts Opts) error {
cfgs, caller, err := utils.LoadConfigs(ctx, opts.profile)
if err != nil {
return fmt.Errorf("loading configs: %s", err)
}
storage, err := scanner.NewStorage(ctx, opts.name)
if err != nil {
return fmt.Errorf("new storage: %s", err)
}
defer storage.Close()
scan := scanner.NewScanner(&scanner.NewScannerInput{
Storage: storage,
Force: opts.force,
Plugins: LoadAllPlugins(cfgs, caller),
})
scanData, err := arn.GetArns(ctx, &arn.GetArnsInput{
AccountsStr: opts.accountsStr,
AccountsPath: opts.accountsPath,
RolePaths: strings.Split(opts.rolesPath, ","),
Regions: utils.GetInputFromPath(regionsList),
})
if err != nil {
return fmt.Errorf("getting scanData: %s", err)
}
for principalArn, exists := range scan.ScanArns(ctx, lo.Keys(scanData)) {
if exists {
fmt.Println(principalArn, "#", scanData[principalArn].Comment)
}
}
if err := storage.Save(); err != nil {
return fmt.Errorf("saving storage: %s", err)
}
return nil
}
func CleanUp(ctx *utils.Context, opts Opts) error {
cfgs, caller, err := utils.LoadConfigs(ctx, opts.profile)
if err != nil {
return fmt.Errorf("loading configs: %s", err)
}
scan := scanner.NewScanner(&scanner.NewScannerInput{
Plugins: LoadAllPlugins(cfgs, caller),
})
if err := scan.CleanUp(ctx); err != nil {
return fmt.Errorf("cleaning up: %s", err)
}
return nil
}
// Setup runs a one-time account optimization
func Setup(ctx *utils.Context, opts Opts) error {
ctx.Info.Printf("Running one-time account optimization")
cfg, err := config.LoadDefaultConfig(ctx.Context, config.WithRegion("us-east-1"), config.WithSharedConfigProfile(opts.profile))
if err != nil {
return fmt.Errorf("loading config: %s", err)
}
ctx.Info.Printf("Enabling all regions, this can take a while...")
if err := utils.EnableAllRegions(ctx, cfg); err != nil {
return fmt.Errorf("enabling all regions: %s", err)
}
cfgs, caller, err := utils.LoadConfigs(ctx, opts.profile)
if err != nil {
return fmt.Errorf("loading configs: %s", err)
}
scan := scanner.NewScanner(&scanner.NewScannerInput{
Force: opts.force,
Plugins: LoadAllPlugins(cfgs, caller),
})
ctx.Info.Printf("Setting up plugins")
scan.SetupPlugins(ctx)
return nil
}