-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathaws.go
More file actions
219 lines (182 loc) · 6.27 KB
/
Copy pathaws.go
File metadata and controls
219 lines (182 loc) · 6.27 KB
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
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
package pluginsdk
import (
"context"
"fmt"
"os"
"path/filepath"
"regexp"
"github.com/aws/aws-sdk-go-v2/aws"
"github.com/aws/aws-sdk-go-v2/config"
"github.com/aws/aws-sdk-go-v2/credentials"
"github.com/aws/aws-sdk-go-v2/credentials/stscreds"
"github.com/aws/aws-sdk-go-v2/service/sts"
"sigs.k8s.io/yaml"
)
type AWSCredentials struct {
UseDefault bool `json:"use_default,omitempty" description:"Use AWS credentials from environment or default profile (recommended)" default:"true"`
ID string `json:"id,omitempty" description:"AWS access key ID"`
Secret string `json:"secret,omitempty" description:"AWS secret access key" sensitive:"true"`
Token string `json:"token,omitempty" description:"AWS session token" sensitive:"true"`
Profile string `json:"profile,omitempty" description:"AWS profile to use from shared credentials file"`
Role string `json:"role,omitempty" description:"AWS IAM role ARN to assume"`
RoleExternalID string `json:"role_external_id,omitempty" description:"External ID for cross-account role assumption"`
Region string `json:"region,omitempty" description:"AWS region for services"`
Endpoint string `json:"endpoint,omitempty" description:"Custom endpoint URL for AWS services" validate:"omitempty,url"`
}
type AWSConfig struct {
Credentials AWSCredentials `json:"credentials" description:"AWS credentials configuration"`
TagsToMetadata bool `json:"tags_to_metadata,omitempty" description:"Convert AWS tags to Marmot metadata"`
IncludeTags []string `json:"include_tags,omitempty" description:"List of AWS tags to include as metadata. By default, all tags are included."`
}
func (a *AWSConfig) Validate() error {
return nil
}
var ErrEndpointNotFound = fmt.Errorf("endpoint not found")
func ExtractAWSConfig(rawConfig map[string]interface{}) (*AWSConfig, error) {
var awsCfg AWSConfig
configBytes, err := yaml.Marshal(rawConfig)
if err != nil {
return nil, fmt.Errorf("marshaling raw config: %w", err)
}
if err := yaml.Unmarshal(configBytes, &awsCfg); err != nil {
return nil, fmt.Errorf("unmarshaling into AWSConfig: %w", err)
}
return &awsCfg, nil
}
// DetectAWSCredentials checks if AWS credentials are available from environment or config files
func DetectAWSCredentials(ctx context.Context) *AWSCredentialStatus {
status := &AWSCredentialStatus{
Available: false,
Sources: []string{},
}
if os.Getenv("AWS_ACCESS_KEY_ID") != "" && os.Getenv("AWS_SECRET_ACCESS_KEY") != "" {
status.Available = true
status.Sources = append(status.Sources, "environment variables")
}
homeDir, err := os.UserHomeDir()
if err == nil {
credsPath := filepath.Join(homeDir, ".aws", "credentials")
if _, err := os.Stat(credsPath); err == nil {
status.Available = true
status.Sources = append(status.Sources, "credentials file (~/.aws/credentials)")
}
}
if err == nil {
configPath := filepath.Join(homeDir, ".aws", "config")
if _, err := os.Stat(configPath); err == nil {
if !contains(status.Sources, "credentials file (~/.aws/credentials)") {
status.Available = true
status.Sources = append(status.Sources, "config file (~/.aws/config)")
}
}
}
if os.Getenv("AWS_CONTAINER_CREDENTIALS_RELATIVE_URI") != "" || os.Getenv("AWS_CONTAINER_CREDENTIALS_FULL_URI") != "" {
status.Available = true
status.Sources = append(status.Sources, "container credentials")
}
if status.Available {
_, err := config.LoadDefaultConfig(ctx)
if err != nil {
status.Available = false
status.Error = err.Error()
}
}
return status
}
type AWSCredentialStatus struct {
Available bool `json:"available"`
Sources []string `json:"sources"`
Error string `json:"error,omitempty"`
} // @name AWSCredentialStatus
func contains(slice []string, item string) bool {
for _, s := range slice {
if s == item {
return true
}
}
return false
}
func (a *AWSConfig) NewAWSConfig(ctx context.Context) (aws.Config, error) {
var opts []func(*config.LoadOptions) error
if a.Credentials.Region != "" {
opts = append(opts, config.WithRegion(a.Credentials.Region))
}
if a.Credentials.UseDefault || (a.Credentials.ID == "" && a.Credentials.Profile == "") {
if a.Credentials.Profile != "" {
opts = append(opts, config.WithSharedConfigProfile(a.Credentials.Profile))
}
} else {
if a.Credentials.ID != "" && a.Credentials.Secret != "" {
provider := credentials.NewStaticCredentialsProvider(
a.Credentials.ID,
a.Credentials.Secret,
a.Credentials.Token,
)
opts = append(opts, config.WithCredentialsProvider(provider))
}
if a.Credentials.Profile != "" {
opts = append(opts, config.WithSharedConfigProfile(a.Credentials.Profile))
}
}
cfg, err := config.LoadDefaultConfig(ctx, opts...)
if err != nil {
return aws.Config{}, fmt.Errorf("loading AWS config: %w", err)
}
if a.Credentials.Role != "" {
stsClient := sts.NewFromConfig(cfg)
assumeRoleOpts := func(o *stscreds.AssumeRoleOptions) {
if a.Credentials.RoleExternalID != "" {
o.ExternalID = aws.String(a.Credentials.RoleExternalID)
}
}
provider := stscreds.NewAssumeRoleProvider(stsClient, a.Credentials.Role, assumeRoleOpts)
cfg.Credentials = aws.NewCredentialsCache(provider)
}
if a.Credentials.Endpoint != "" {
cfg.BaseEndpoint = aws.String(a.Credentials.Endpoint)
}
return cfg, nil
}
func ProcessAWSTags(tagsToMetadata bool, includeTags []string, tags map[string]string) map[string]interface{} {
metadata := make(map[string]interface{})
if !tagsToMetadata || len(tags) == 0 {
return metadata
}
for key, value := range tags {
if len(includeTags) > 0 {
included := false
for _, includeTag := range includeTags {
if key == includeTag {
included = true
break
}
}
if !included {
continue
}
}
metadata[fmt.Sprintf("tag_%s", key)] = value
}
return metadata
}
func ShouldIncludeResource(name string, filter Filter) bool {
if len(filter.Include) == 0 && len(filter.Exclude) == 0 {
return true
}
for _, pattern := range filter.Exclude {
matched, err := regexp.MatchString(pattern, name)
if err == nil && matched {
return false
}
}
if len(filter.Include) == 0 {
return true
}
for _, pattern := range filter.Include {
matched, err := regexp.MatchString(pattern, name)
if err == nil && matched {
return true
}
}
return false
}