This repository has been archived by the owner on Apr 1, 2024. It is now read-only.
/
config.go
167 lines (146 loc) · 4.6 KB
/
config.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
package config
import (
"io/ioutil"
"time"
"github.com/chanzuckerberg/reaper/pkg/policy"
"github.com/pkg/errors"
"github.com/spf13/afero"
yaml "gopkg.in/yaml.v2"
"k8s.io/apimachinery/pkg/labels"
)
// TypeResource describes the type of resource
type TypeResource string
// Duration because I really love writing time un/marshal logic
type Duration time.Duration
// UnmarshalYAML custom unmarshal logic
func (d *Duration) UnmarshalYAML(unmarshal func(v interface{}) error) error {
var s string
err := unmarshal(&s)
if err != nil {
return errors.Wrap(err, "yaml: Unmarshal error")
}
t, err := time.ParseDuration(s)
if err != nil {
return errors.Wrapf(err, "Could not parse duration: %s", s)
}
*d = Duration(t)
return nil
}
// Duration returns our custom type as a time.Duration handling nils when needed
func (d *Duration) Duration() *time.Duration {
if d == nil {
return nil
}
duration := time.Duration(*d)
return &duration
}
// NotificationConfig is a notification config
type NotificationConfig struct {
Recipient string `yaml:"recipient"`
MessageTemplate string `yaml:"message_template"`
}
// PolicyConfig is the configuration for a policy
type PolicyConfig struct {
Name string `yaml:"name"`
ResourceSelector string `yaml:"resource_selector"`
TagSelector *string `yaml:"tag_selector"`
LabelSelector *string `yaml:"label_selector"`
// MaxAge for this resource
// If it matches the policy and exceeds MaxAge remediation will be taken.
MaxAge *Duration `yaml:"max_age"`
Notifications NotificationsConfig `yaml:"notifications"`
}
type NotificationsConfig struct {
Warnings []NotificationConfig `yaml:"warnings"`
}
//AccountConfig identifies an AWS account we want to monitor
type AccountConfig struct {
Name string `yaml:"name"`
ID int64 `yaml:"id"`
Role string `yaml:"role"`
Owner string `yaml:"owner"`
}
//IdentityMapConfig will allow mapping group email lists to slack channels
type IdentityMapConfig struct {
Email string `yaml:"email"`
Slack string `yaml:"slack"`
}
// Config is the configuration
type Config struct {
Version int `yaml:version`
Policies []PolicyConfig `yaml:"policies"`
AWSRegions []string `yaml:"aws_regions"`
Accounts []AccountConfig `yaml:"accounts"`
IdentityMap []IdentityMapConfig `yaml:"identity_map"`
}
// GetPolicies gets the policies from a config
func (c *Config) GetPolicies() ([]policy.Policy, error) {
policies := make([]policy.Policy, len(c.Policies))
for i, cp := range c.Policies {
rs, err := labels.Parse(cp.ResourceSelector)
if err != nil {
return nil, errors.Wrapf(err, "Invalid selector: %s", cp.ResourceSelector)
}
var ls labels.Selector
if cp.LabelSelector != nil {
ls, err = labels.Parse(*cp.LabelSelector)
if err != nil {
return nil, errors.Wrapf(err, "Invalid selector: %s", *cp.LabelSelector)
}
}
var ts labels.Selector
if cp.TagSelector != nil {
ts, err = labels.Parse(*cp.TagSelector)
if err != nil {
return nil, errors.Wrapf(err, "Invalid selector: %s", *cp.TagSelector)
}
}
notifications := make([]policy.Notification, len(cp.Notifications.Warnings))
for j, n := range cp.Notifications.Warnings {
notification := policy.Notification{}
notification.MessageTemplate = n.MessageTemplate
notification.Recipient = n.Recipient
notifications[j] = notification
}
p := policy.Policy{
Name: cp.Name,
ResourceSelector: rs,
LabelSelector: ls,
TagSelector: ts,
MaxAge: cp.MaxAge.Duration(),
Notifications: notifications,
}
policies[i] = p
}
return policies, nil
}
//GetAccounts will return policy.Account objects
func (c *Config) GetAccounts() ([]*policy.Account, error) {
var accounts []*policy.Account
for _, a := range c.Accounts {
accounts = append(accounts, &policy.Account{Name: a.Name, ID: a.ID, Role: a.Role, Owner: a.Owner})
}
return accounts, nil
}
// GetIdentityMap will return a map of email -> slack identifier
func (c *Config) GetIdentityMap() (map[string]string, error) {
m := make(map[string]string)
for _, i := range c.IdentityMap {
m[i.Email] = i.Slack
}
return m, nil
}
// FromFile reads a config from a file
func FromFile(fs afero.Fs, fileName string) (*Config, error) {
f, err := fs.Open(fileName)
if err != nil {
return nil, errors.Wrapf(err, "Could not open file %s", fileName)
}
bytes, err := ioutil.ReadAll(f)
if err != nil {
return nil, errors.Wrapf(err, "Could not read config file %s contents", fileName)
}
config := &Config{}
err = yaml.Unmarshal(bytes, config)
return config, errors.Wrapf(err, "Could not Unmarshal config %s", fileName)
}