diff options
author | Mike Crute <mike@crute.us> | 2022-12-24 08:30:27 -0800 |
---|---|---|
committer | Mike Crute <mike@crute.us> | 2022-12-24 08:30:27 -0800 |
commit | 4aac2b6026d27d4eba660b674bdb1f34bcfbfc54 (patch) | |
tree | 62eb040afb6d7f56f9bfb0af69cfc17ec2060a8b | |
parent | ed1504c2826f6a5d406dd72e51f5a90b77ffea45 (diff) | |
download | cloud-identity-broker-4aac2b6026d27d4eba660b674bdb1f34bcfbfc54.tar.bz2 cloud-identity-broker-4aac2b6026d27d4eba660b674bdb1f34bcfbfc54.tar.xz cloud-identity-broker-4aac2b6026d27d4eba660b674bdb1f34bcfbfc54.zip |
Use Vault for IAM users
-rw-r--r-- | app/controllers/api_account.go | 8 | ||||
-rw-r--r-- | app/controllers/aws.go | 51 | ||||
-rw-r--r-- | app/models/account.go | 12 | ||||
-rw-r--r-- | app/models/session_key.go | 2 | ||||
-rw-r--r-- | cloud/aws/aws.go | 137 | ||||
-rw-r--r-- | cmd/web/server.go | 12 | ||||
-rw-r--r-- | go.mod | 5 | ||||
-rw-r--r-- | go.sum | 9 |
8 files changed, 128 insertions, 108 deletions
diff --git a/app/controllers/api_account.go b/app/controllers/api_account.go index 815daf4..cecd334 100644 --- a/app/controllers/api_account.go +++ b/app/controllers/api_account.go | |||
@@ -2,14 +2,12 @@ package controllers | |||
2 | 2 | ||
3 | import ( | 3 | import ( |
4 | "context" | 4 | "context" |
5 | "fmt" | ||
6 | "net/http" | 5 | "net/http" |
7 | "time" | 6 | "time" |
8 | 7 | ||
9 | "code.crute.us/mcrute/cloud-identity-broker/app" | 8 | "code.crute.us/mcrute/cloud-identity-broker/app" |
10 | "code.crute.us/mcrute/cloud-identity-broker/app/middleware" | 9 | "code.crute.us/mcrute/cloud-identity-broker/app/middleware" |
11 | "code.crute.us/mcrute/cloud-identity-broker/app/models" | 10 | "code.crute.us/mcrute/cloud-identity-broker/app/models" |
12 | "code.crute.us/mcrute/cloud-identity-broker/cloud/aws" | ||
13 | 11 | ||
14 | glecho "code.crute.us/mcrute/golib/echo" | 12 | glecho "code.crute.us/mcrute/golib/echo" |
15 | "code.crute.us/mcrute/golib/echo/controller" | 13 | "code.crute.us/mcrute/golib/echo/controller" |
@@ -90,7 +88,7 @@ func (h *APIAccountHandler) HandleGet(c echo.Context) error { | |||
90 | // details about the account so they should only be visible to users who | 88 | // details about the account so they should only be visible to users who |
91 | // can administer the account. | 89 | // can administer the account. |
92 | if !a.CanBeModifiedBy(p) { | 90 | if !a.CanBeModifiedBy(p) { |
93 | a.VaultMaterial = "" | 91 | a.AdminVaultMaterial = "" |
94 | a.Users = nil | 92 | a.Users = nil |
95 | } | 93 | } |
96 | 94 | ||
@@ -136,7 +134,7 @@ func (h *APIAccountHandler) HandlePut(c echo.Context) error { | |||
136 | a.AccountNumber = in.AccountNumber | 134 | a.AccountNumber = in.AccountNumber |
137 | a.Name = in.Name | 135 | a.Name = in.Name |
138 | a.ConsoleSessionDuration = in.ConsoleSessionDuration | 136 | a.ConsoleSessionDuration = in.ConsoleSessionDuration |
139 | a.VaultMaterial = in.VaultMaterial | 137 | a.AdminVaultMaterial = in.AdminVaultMaterial |
140 | a.DefaultRegion = in.DefaultRegion | 138 | a.DefaultRegion = in.DefaultRegion |
141 | a.Users = in.Users | 139 | a.Users = in.Users |
142 | 140 | ||
@@ -181,12 +179,14 @@ func (h *APIAccountHandler) HandlePost(c echo.Context) error { | |||
181 | } | 179 | } |
182 | } | 180 | } |
183 | 181 | ||
182 | /* TODO: Validate that the vault material exists | ||
184 | if err := aws.ValidateVaultMaterial(in.VaultMaterial); err != nil { | 183 | if err := aws.ValidateVaultMaterial(in.VaultMaterial); err != nil { |
185 | return &echo.HTTPError{ | 184 | return &echo.HTTPError{ |
186 | Code: http.StatusBadRequest, | 185 | Code: http.StatusBadRequest, |
187 | Message: fmt.Sprintf("Unable to access Vault material: %s", err), | 186 | Message: fmt.Sprintf("Unable to access Vault material: %s", err), |
188 | } | 187 | } |
189 | } | 188 | } |
189 | */ | ||
190 | 190 | ||
191 | if err := h.Store.Put(context.Background(), &in); err != nil { | 191 | if err := h.Store.Put(context.Background(), &in); err != nil { |
192 | return echo.ErrInternalServerError | 192 | return echo.ErrInternalServerError |
diff --git a/app/controllers/aws.go b/app/controllers/aws.go index 5b1765d..4f32942 100644 --- a/app/controllers/aws.go +++ b/app/controllers/aws.go | |||
@@ -2,11 +2,14 @@ package controllers | |||
2 | 2 | ||
3 | import ( | 3 | import ( |
4 | "context" | 4 | "context" |
5 | "sync" | ||
5 | 6 | ||
6 | "code.crute.us/mcrute/cloud-identity-broker/app/middleware" | 7 | "code.crute.us/mcrute/cloud-identity-broker/app/middleware" |
7 | "code.crute.us/mcrute/cloud-identity-broker/app/models" | 8 | "code.crute.us/mcrute/cloud-identity-broker/app/models" |
8 | "code.crute.us/mcrute/cloud-identity-broker/cloud/aws" | 9 | "code.crute.us/mcrute/cloud-identity-broker/cloud/aws" |
9 | 10 | ||
11 | "code.crute.us/mcrute/golib/secrets" | ||
12 | |||
10 | "github.com/labstack/echo/v4" | 13 | "github.com/labstack/echo/v4" |
11 | ) | 14 | ) |
12 | 15 | ||
@@ -20,7 +23,49 @@ type requestContext struct { | |||
20 | // This capability does common permission checks and populates a request | 23 | // This capability does common permission checks and populates a request |
21 | // context with user, account, and AWS API information. | 24 | // context with user, account, and AWS API information. |
22 | type AWSAPI struct { | 25 | type AWSAPI struct { |
23 | Store models.AccountStore | 26 | Store models.AccountStore |
27 | Secrets secrets.Client | ||
28 | cache sync.Map // of aws.AWSClient | ||
29 | } | ||
30 | |||
31 | func (h *AWSAPI) getClientFor(ctx context.Context, a *models.Account) (aws.AWSClient, error) { | ||
32 | var client aws.AWSClient | ||
33 | |||
34 | if cv, ok := h.cache.Load(a.ShortName); !ok { | ||
35 | client, err := aws.NewAWSClientFromAccount(ctx, a, h.Secrets) | ||
36 | if err != nil { | ||
37 | return nil, err | ||
38 | } | ||
39 | |||
40 | cv, _ = h.cache.LoadOrStore(a.ShortName, client) | ||
41 | client = cv.(aws.AWSClient) | ||
42 | } else { | ||
43 | client = cv.(aws.AWSClient) | ||
44 | } | ||
45 | |||
46 | return client, nil | ||
47 | } | ||
48 | |||
49 | // Preload enumerates all managed accounts and pre-loads all of the | ||
50 | // clients into the cache. | ||
51 | // | ||
52 | // This exists because there is replication delay of around 5-10 seconds | ||
53 | // for IAM users into other regions so these should be created as early | ||
54 | // in the process lifecycle as possible. | ||
55 | func (h *AWSAPI) Preload(ctx context.Context) []error { | ||
56 | accounts, err := h.Store.List(ctx) | ||
57 | if err != nil { | ||
58 | return []error{err} | ||
59 | } | ||
60 | |||
61 | errors := []error{} | ||
62 | for _, a := range accounts { | ||
63 | _, err = h.getClientFor(ctx, a) | ||
64 | if err != nil { | ||
65 | errors = append(errors, err) | ||
66 | } | ||
67 | } | ||
68 | return errors | ||
24 | } | 69 | } |
25 | 70 | ||
26 | // GetContext checks that the user is authenticated and is authorized to access | 71 | // GetContext checks that the user is authenticated and is authorized to access |
@@ -38,7 +83,7 @@ func (h *AWSAPI) GetContext(c echo.Context) (*requestContext, error) { | |||
38 | return nil, echo.NotFoundHandler(c) | 83 | return nil, echo.NotFoundHandler(c) |
39 | } | 84 | } |
40 | 85 | ||
41 | ac, err := aws.NewAWSClientFromAccount(account) | 86 | client, err := h.getClientFor(c.Request().Context(), account) |
42 | if err != nil { | 87 | if err != nil { |
43 | c.Logger().Errorf("Error building AWS client: %w", err) | 88 | c.Logger().Errorf("Error building AWS client: %w", err) |
44 | return nil, echo.ErrInternalServerError | 89 | return nil, echo.ErrInternalServerError |
@@ -47,6 +92,6 @@ func (h *AWSAPI) GetContext(c echo.Context) (*requestContext, error) { | |||
47 | return &requestContext{ | 92 | return &requestContext{ |
48 | Account: account, | 93 | Account: account, |
49 | Principal: principal, | 94 | Principal: principal, |
50 | AWS: ac, | 95 | AWS: client, |
51 | }, nil | 96 | }, nil |
52 | } | 97 | } |
diff --git a/app/models/account.go b/app/models/account.go index af29c3e..346354c 100644 --- a/app/models/account.go +++ b/app/models/account.go | |||
@@ -25,8 +25,9 @@ type Account struct { | |||
25 | AccountType string `json:"account_type"` | 25 | AccountType string `json:"account_type"` |
26 | AccountNumber int `json:"account_number"` | 26 | AccountNumber int `json:"account_number"` |
27 | Name string `json:"name"` | 27 | Name string `json:"name"` |
28 | ConsoleSessionDuration time.Duration `json:"console_session_duration, omitempty"` | 28 | ConsoleSessionDuration time.Duration `json:"console_session_duration,omitempty"` |
29 | VaultMaterial string `json:"vault_material,omitempty"` | 29 | AdminVaultMaterial string `json:"admin_vault_material,omitempty"` |
30 | AssumedRoleARN string `json:"assumed_role_arn"` | ||
30 | DefaultRegion string `json:"default_region"` | 31 | DefaultRegion string `json:"default_region"` |
31 | Users []string `json:"users,omitempty"` | 32 | Users []string `json:"users,omitempty"` |
32 | Deleted *time.Time `json:"deleted,omitempty" bson:"deleted,omitempty"` | 33 | Deleted *time.Time `json:"deleted,omitempty" bson:"deleted,omitempty"` |
@@ -43,10 +44,9 @@ func (a *Account) CanBeModifiedBy(u *User) bool { | |||
43 | type MongoDbAccountStore struct { | 44 | type MongoDbAccountStore struct { |
44 | Db *mongodb.Mongo | 45 | Db *mongodb.Mongo |
45 | 46 | ||
46 | // ReturnDeleted will allow all methods to return deleted items. By default | 47 | // ReturnDeleted will allow all methods to return deleted items. items |
47 | // items where the Deleted field is set will not be returned. This should | 48 | // where the Deleted field is set will not be returned. Non-admin |
48 | // be the common cast for most code using this store but in some Admin | 49 | // use-cases should leave this set to false. |
49 | // use-cases it would be useful to show deleted accounts. | ||
50 | ReturnDeleted bool | 50 | ReturnDeleted bool |
51 | } | 51 | } |
52 | 52 | ||
diff --git a/app/models/session_key.go b/app/models/session_key.go index c8b327e..b75d6c4 100644 --- a/app/models/session_key.go +++ b/app/models/session_key.go | |||
@@ -52,7 +52,7 @@ type SessionKey struct { | |||
52 | NotBefore *time.Time | 52 | NotBefore *time.Time |
53 | PublicKey crypto.PublicKey | 53 | PublicKey crypto.PublicKey |
54 | PrivateKey *ecdsa.PrivateKey | 54 | PrivateKey *ecdsa.PrivateKey |
55 | ExposePrivateKeysInJSON bool `"-" json:"-" bson:"-"` | 55 | ExposePrivateKeysInJSON bool `json:"-" bson:"-"` |
56 | } | 56 | } |
57 | 57 | ||
58 | func GenerateSessionKey(ttl time.Duration) (*SessionKey, error) { | 58 | func GenerateSessionKey(ttl time.Duration) (*SessionKey, error) { |
diff --git a/cloud/aws/aws.go b/cloud/aws/aws.go index 36ac338..3229dd2 100644 --- a/cloud/aws/aws.go +++ b/cloud/aws/aws.go | |||
@@ -1,6 +1,7 @@ | |||
1 | package aws | 1 | package aws |
2 | 2 | ||
3 | import ( | 3 | import ( |
4 | "context" | ||
4 | "encoding/json" | 5 | "encoding/json" |
5 | "fmt" | 6 | "fmt" |
6 | "io/ioutil" | 7 | "io/ioutil" |
@@ -9,9 +10,10 @@ import ( | |||
9 | "strconv" | 10 | "strconv" |
10 | 11 | ||
11 | "code.crute.us/mcrute/cloud-identity-broker/app/models" | 12 | "code.crute.us/mcrute/cloud-identity-broker/app/models" |
13 | "code.crute.us/mcrute/golib/secrets" | ||
12 | 14 | ||
13 | "code.crute.us/mcrute/golib/vault" | ||
14 | "github.com/aws/aws-sdk-go/aws" | 15 | "github.com/aws/aws-sdk-go/aws" |
16 | "github.com/aws/aws-sdk-go/aws/awserr" | ||
15 | "github.com/aws/aws-sdk-go/aws/credentials" | 17 | "github.com/aws/aws-sdk-go/aws/credentials" |
16 | "github.com/aws/aws-sdk-go/aws/session" | 18 | "github.com/aws/aws-sdk-go/aws/session" |
17 | "github.com/aws/aws-sdk-go/service/ec2" | 19 | "github.com/aws/aws-sdk-go/service/ec2" |
@@ -27,28 +29,18 @@ type Region struct { | |||
27 | Enabled bool | 29 | Enabled bool |
28 | } | 30 | } |
29 | 31 | ||
32 | // AWSClient is a client for working with the AWS APIs. | ||
33 | // | ||
34 | // Instances of AWSClient are safe for concurrent access. | ||
30 | type AWSClient interface { | 35 | type AWSClient interface { |
31 | AssumeRole(string, *string) (*sts.Credentials, error) | 36 | AssumeRole(string, *string) (*sts.Credentials, error) |
32 | GetFederationURL(string, string) (string, error) | 37 | GetFederationURL(string, string) (string, error) |
33 | GetRegionList() ([]*Region, error) | 38 | GetRegionList() ([]*Region, error) |
34 | } | 39 | } |
35 | 40 | ||
36 | // account models the account configuration stored in Vault for an AWS account | ||
37 | // with assumable roles that are stored within a kv JSON record. | ||
38 | type account struct { | ||
39 | AccessKeyId string | ||
40 | SecretAccessKey string | ||
41 | Roles map[string]struct { | ||
42 | ARN string | ||
43 | ExternalId string | ||
44 | } | ||
45 | } | ||
46 | |||
47 | type client struct { | 41 | type client struct { |
48 | AccessKeyId string | 42 | Credentials credentials.Value |
49 | SecretAccessKey string | ||
50 | ARN string | 43 | ARN string |
51 | ExternalId string | ||
52 | ConsoleSessionDurationSecs int64 | 44 | ConsoleSessionDurationSecs int64 |
53 | } | 45 | } |
54 | 46 | ||
@@ -62,61 +54,22 @@ var _ AWSClient = (*client)(nil) | |||
62 | // which is used as the scope for this AWS client. Thus even if an account has | 54 | // which is used as the scope for this AWS client. Thus even if an account has |
63 | // multiple roles there must be one instance of the AWS client per account/role | 55 | // multiple roles there must be one instance of the AWS client per account/role |
64 | // pair. | 56 | // pair. |
65 | func NewAWSClientFromAccount(a *models.Account) (AWSClient, error) { | 57 | func NewAWSClientFromAccount(ctx context.Context, a *models.Account, sc secrets.Client) (AWSClient, error) { |
66 | var ac account | 58 | u, _, err := sc.AWSIAMUser(ctx, a.AdminVaultMaterial) |
67 | if err := vault.GetVaultKeyStruct(a.VaultMaterial, &ac); err != nil { | 59 | if err != nil { |
68 | return nil, err | 60 | return nil, err |
69 | } | 61 | } |
70 | 62 | ||
71 | r, ok := ac.Roles[a.ShortName] | ||
72 | if !ok { | ||
73 | return nil, fmt.Errorf("No roles for account %s in vault response", a.ShortName) | ||
74 | } | ||
75 | |||
76 | return &client{ | 63 | return &client{ |
77 | AccessKeyId: ac.AccessKeyId, | 64 | Credentials: credentials.Value{ |
78 | SecretAccessKey: ac.SecretAccessKey, | 65 | AccessKeyID: u.AccessKeyId, |
79 | ARN: r.ARN, | 66 | SecretAccessKey: u.SecretAccessKey, |
80 | ExternalId: r.ExternalId, | 67 | }, |
68 | ARN: a.AssumedRoleARN, | ||
81 | ConsoleSessionDurationSecs: a.ConsoleSessionDurationSecs(), | 69 | ConsoleSessionDurationSecs: a.ConsoleSessionDurationSecs(), |
82 | }, nil | 70 | }, nil |
83 | } | 71 | } |
84 | 72 | ||
85 | // ValidateVaultMaterial is used to check that a Vault material can be accessed | ||
86 | // and that the shape of that material is correct for an AWS access key and | ||
87 | // role list. | ||
88 | // | ||
89 | // This should be used for admission control for the creation of new accounts. | ||
90 | func ValidateVaultMaterial(m string) error { | ||
91 | var ac account | ||
92 | if err := vault.GetVaultKeyStruct(m, &ac); err != nil { | ||
93 | return fmt.Errorf("Unable to access vault material: %w", err) | ||
94 | } | ||
95 | |||
96 | if ac.AccessKeyId == "" { | ||
97 | return fmt.Errorf("AccessKeyId is empty") | ||
98 | } | ||
99 | |||
100 | if ac.SecretAccessKey == "" { | ||
101 | return fmt.Errorf("SecretAccessKey is empty") | ||
102 | } | ||
103 | |||
104 | if len(ac.Roles) == 0 { | ||
105 | return fmt.Errorf("No roles specified") | ||
106 | } | ||
107 | |||
108 | for k, r := range ac.Roles { | ||
109 | if r.ARN == "" { | ||
110 | return fmt.Errorf("ARN for role %s is empty", k) | ||
111 | } | ||
112 | if r.ExternalId == "" { | ||
113 | return fmt.Errorf("ExternalId for role %s is empty", k) | ||
114 | } | ||
115 | } | ||
116 | |||
117 | return nil | ||
118 | } | ||
119 | |||
120 | // AssumeRole uses an IAM user credential with higher privilege to assume a | 73 | // AssumeRole uses an IAM user credential with higher privilege to assume a |
121 | // role in an AWS account and region. It returns the STS credentials. | 74 | // role in an AWS account and region. It returns the STS credentials. |
122 | // | 75 | // |
@@ -124,32 +77,34 @@ func ValidateVaultMaterial(m string) error { | |||
124 | // regions AWS has been siloing assumed role credentials to that region so it's | 77 | // regions AWS has been siloing assumed role credentials to that region so it's |
125 | // important to use the correct regional endpoint to fetch the credentials. | 78 | // important to use the correct regional endpoint to fetch the credentials. |
126 | // | 79 | // |
127 | // Note that this is not simply a passthrough to Vault's AWS backend because | 80 | // Note that this is not simply a passthrough to Vault's AWS backend |
128 | // the Vault backend works by assuming roles and when assuming roles with an | 81 | // because the Vault backend will only call AssumeRole within the region |
129 | // assumed role AWS limits the chained role lifetime to 1 hour which doesn't | 82 | // of the root account configured in Vault and clients of this method |
130 | // work depending on how the upstream web application tied to this client | 83 | // need to be able to make the correct calls in the region of their |
131 | // works. This method instead uses a long-lived IAM user credential to assume a | 84 | // choice. |
132 | // role, which has a limited lifetime which is typically greater than 1 hour. | ||
133 | func (a *client) AssumeRole(user string, region *string) (*sts.Credentials, error) { | 85 | func (a *client) AssumeRole(user string, region *string) (*sts.Credentials, error) { |
134 | if region != nil && *region == "global" { | 86 | if region != nil && *region == "global" { |
135 | region = nil | 87 | region = nil |
136 | } | 88 | } |
137 | 89 | ||
138 | c := sts.New(session.New(&aws.Config{ | 90 | s, err := session.NewSessionWithOptions(session.Options{ |
139 | Region: region, | 91 | Config: aws.Config{ |
140 | Credentials: credentials.NewStaticCredentials( | 92 | Region: region, |
141 | a.AccessKeyId, | 93 | Credentials: credentials.NewStaticCredentialsFromCreds(a.Credentials), |
142 | a.SecretAccessKey, | 94 | }, |
143 | "", | 95 | SharedConfigState: session.SharedConfigDisable, |
144 | ), | 96 | }) |
145 | })) | 97 | if err != nil { |
146 | result, err := c.AssumeRole(&sts.AssumeRoleInput{ | 98 | return nil, err |
147 | ExternalId: aws.String(a.ExternalId), | 99 | } |
100 | |||
101 | result, err := sts.New(s).AssumeRole(&sts.AssumeRoleInput{ | ||
148 | RoleArn: aws.String(a.ARN), | 102 | RoleArn: aws.String(a.ARN), |
149 | RoleSessionName: aws.String(user), | 103 | RoleSessionName: aws.String(user), |
150 | DurationSeconds: aws.Int64(a.ConsoleSessionDurationSecs), | 104 | DurationSeconds: aws.Int64(a.ConsoleSessionDurationSecs), |
151 | }) | 105 | }) |
152 | if err != nil { | 106 | if err != nil { |
107 | fmt.Printf("AWS AssumeRole Error: %s\n", err.(awserr.Error).Message()) | ||
153 | return nil, err | 108 | return nil, err |
154 | } | 109 | } |
155 | 110 | ||
@@ -167,16 +122,24 @@ func (a *client) GetRegionList() ([]*Region, error) { | |||
167 | return nil, err | 122 | return nil, err |
168 | } | 123 | } |
169 | 124 | ||
170 | ec2c := ec2.New(session.New(&aws.Config{ | 125 | s, err := session.NewSessionWithOptions(session.Options{ |
171 | Region: defaultRegion, | 126 | Config: aws.Config{ |
172 | Credentials: credentials.NewStaticCredentials( | 127 | Region: defaultRegion, |
173 | *r.AccessKeyId, | 128 | Credentials: credentials.NewStaticCredentials( |
174 | *r.SecretAccessKey, | 129 | *r.AccessKeyId, |
175 | *r.SessionToken, | 130 | *r.SecretAccessKey, |
176 | ), | 131 | *r.SessionToken, |
177 | })) | 132 | ), |
133 | }, | ||
134 | SharedConfigState: session.SharedConfigDisable, | ||
135 | }) | ||
136 | if err != nil { | ||
137 | return nil, err | ||
138 | } | ||
178 | 139 | ||
179 | ro, err := ec2c.DescribeRegions(&ec2.DescribeRegionsInput{AllRegions: aws.Bool(true)}) | 140 | ro, err := ec2.New(s).DescribeRegions(&ec2.DescribeRegionsInput{ |
141 | AllRegions: aws.Bool(true), | ||
142 | }) | ||
180 | if err != nil { | 143 | if err != nil { |
181 | return nil, err | 144 | return nil, err |
182 | } | 145 | } |
diff --git a/cmd/web/server.go b/cmd/web/server.go index 9c29544..82e0f75 100644 --- a/cmd/web/server.go +++ b/cmd/web/server.go | |||
@@ -130,7 +130,17 @@ func setupApplication(ctx context.Context, cfg app.Config, s *glecho.EchoWrapper | |||
130 | as := &models.MongoDbAccountStore{Db: mongo} | 130 | as := &models.MongoDbAccountStore{Db: mongo} |
131 | us := &models.MongoDbUserStore{Db: mongo} | 131 | us := &models.MongoDbUserStore{Db: mongo} |
132 | 132 | ||
133 | aws := &controllers.AWSAPI{Store: as} | 133 | aws := &controllers.AWSAPI{ |
134 | Store: as, | ||
135 | Secrets: vc, | ||
136 | } | ||
137 | |||
138 | if errs := aws.Preload(ctx); len(errs) > 0 { | ||
139 | for _, err := range errs { | ||
140 | log.Printf("Error preloading AWS accounts: %s", err) | ||
141 | } | ||
142 | log.Fatalf("Could not preload all AWS accounts") | ||
143 | } | ||
134 | 144 | ||
135 | ghCred := &app.GitHubOauthCreds{} | 145 | ghCred := &app.GitHubOauthCreds{} |
136 | if _, err := vc.Secret(ctx, cfg.GitHubOauthCreds, &ghCred); err != nil { | 146 | if _, err := vc.Secret(ctx, cfg.GitHubOauthCreds, &ghCred); err != nil { |
@@ -3,7 +3,7 @@ module code.crute.us/mcrute/cloud-identity-broker | |||
3 | go 1.18 | 3 | go 1.18 |
4 | 4 | ||
5 | replace ( | 5 | replace ( |
6 | code.crute.us/mcrute/golib => ../golib | 6 | code.crute.us/mcrute/golib/secrets => ../golib/secrets |
7 | golang.org/x/crypto => ../third_party/golang/x/crypto | 7 | golang.org/x/crypto => ../third_party/golang/x/crypto |
8 | ) | 8 | ) |
9 | 9 | ||
@@ -14,7 +14,6 @@ require ( | |||
14 | code.crute.us/mcrute/golib/db/mongodb/v2 v2.0.0 | 14 | code.crute.us/mcrute/golib/db/mongodb/v2 v2.0.0 |
15 | code.crute.us/mcrute/golib/echo v0.9.3 | 15 | code.crute.us/mcrute/golib/echo v0.9.3 |
16 | code.crute.us/mcrute/golib/secrets v0.2.0 | 16 | code.crute.us/mcrute/golib/secrets v0.2.0 |
17 | code.crute.us/mcrute/golib/vault v0.2.4 | ||
18 | github.com/aws/aws-sdk-go v1.42.4 | 17 | github.com/aws/aws-sdk-go v1.42.4 |
19 | github.com/labstack/echo/v4 v4.6.1 | 18 | github.com/labstack/echo/v4 v4.6.1 |
20 | github.com/prometheus/client_golang v1.11.0 | 19 | github.com/prometheus/client_golang v1.11.0 |
@@ -29,6 +28,7 @@ require ( | |||
29 | code.crute.us/mcrute/golib v0.5.1 // indirect | 28 | code.crute.us/mcrute/golib v0.5.1 // indirect |
30 | code.crute.us/mcrute/golib/clients/dns v0.1.0 // indirect | 29 | code.crute.us/mcrute/golib/clients/dns v0.1.0 // indirect |
31 | code.crute.us/mcrute/golib/clients/netbox v0.1.0 // indirect | 30 | code.crute.us/mcrute/golib/clients/netbox v0.1.0 // indirect |
31 | code.crute.us/mcrute/golib/vault v0.2.4 // indirect | ||
32 | github.com/armon/go-metrics v0.3.10 // indirect | 32 | github.com/armon/go-metrics v0.3.10 // indirect |
33 | github.com/armon/go-radix v1.0.0 // indirect | 33 | github.com/armon/go-radix v1.0.0 // indirect |
34 | github.com/beorn7/perks v1.0.1 // indirect | 34 | github.com/beorn7/perks v1.0.1 // indirect |
@@ -96,4 +96,5 @@ require ( | |||
96 | google.golang.org/genproto v0.0.0-20211208223120-3a66f561d7aa // indirect | 96 | google.golang.org/genproto v0.0.0-20211208223120-3a66f561d7aa // indirect |
97 | google.golang.org/grpc v1.42.0 // indirect | 97 | google.golang.org/grpc v1.42.0 // indirect |
98 | google.golang.org/protobuf v1.27.1 // indirect | 98 | google.golang.org/protobuf v1.27.1 // indirect |
99 | gopkg.in/yaml.v2 v2.4.0 // indirect | ||
99 | ) | 100 | ) |
@@ -45,6 +45,8 @@ cloud.google.com/go/storage v1.5.0/go.mod h1:tpKbwo567HUNpVclU5sGELwQWBDZ8gh0Zeo | |||
45 | cloud.google.com/go/storage v1.6.0/go.mod h1:N7U0C8pVQ/+NIKOBQyamJIeKQKkZ+mxpohlUTyfDhBk= | 45 | cloud.google.com/go/storage v1.6.0/go.mod h1:N7U0C8pVQ/+NIKOBQyamJIeKQKkZ+mxpohlUTyfDhBk= |
46 | cloud.google.com/go/storage v1.8.0/go.mod h1:Wv1Oy7z6Yz3DshWRJFhqM/UCfaWIRTdp0RXyy7KQOVs= | 46 | cloud.google.com/go/storage v1.8.0/go.mod h1:Wv1Oy7z6Yz3DshWRJFhqM/UCfaWIRTdp0RXyy7KQOVs= |
47 | cloud.google.com/go/storage v1.10.0/go.mod h1:FLPqc6j+Ki4BU591ie1oL6qBQGu2Bl/tZ9ullr3+Kg0= | 47 | cloud.google.com/go/storage v1.10.0/go.mod h1:FLPqc6j+Ki4BU591ie1oL6qBQGu2Bl/tZ9ullr3+Kg0= |
48 | code.crute.us/mcrute/golib v0.5.1 h1:z66+VQxqas98nPYeGEdJvkHrQwxd2mISVm6qzaBJwF8= | ||
49 | code.crute.us/mcrute/golib v0.5.1/go.mod h1:dukLPhs1H8dxtkhXtpJZYo/bMzefLRbdRj9Tj67wdaQ= | ||
48 | code.crute.us/mcrute/golib/cli v0.2.2 h1:1MgyEYCyZ2oJBs/FrztMmxJoh0v+7j21VsWXBTIWsqw= | 50 | code.crute.us/mcrute/golib/cli v0.2.2 h1:1MgyEYCyZ2oJBs/FrztMmxJoh0v+7j21VsWXBTIWsqw= |
49 | code.crute.us/mcrute/golib/cli v0.2.2/go.mod h1:vc2TpQ5J/3zRfcWq6sclmU0EmJI8xygpOij77VJ8EK8= | 51 | code.crute.us/mcrute/golib/cli v0.2.2/go.mod h1:vc2TpQ5J/3zRfcWq6sclmU0EmJI8xygpOij77VJ8EK8= |
50 | code.crute.us/mcrute/golib/clients/autocert/v2 v2.0.0 h1:MTS65Npib7DFnsNZ5Fs7EYXkK2ITEqdZQ18kBd3FdPk= | 52 | code.crute.us/mcrute/golib/clients/autocert/v2 v2.0.0 h1:MTS65Npib7DFnsNZ5Fs7EYXkK2ITEqdZQ18kBd3FdPk= |
@@ -59,8 +61,6 @@ code.crute.us/mcrute/golib/db/mongodb/v2 v2.0.0 h1:v4AYsbesoDeAMMbwS43WzqywNm0w0 | |||
59 | code.crute.us/mcrute/golib/db/mongodb/v2 v2.0.0/go.mod h1:3dFJwm2MtCb312eHdHnK/w8D1lwgCeewa/2hztw89kE= | 61 | code.crute.us/mcrute/golib/db/mongodb/v2 v2.0.0/go.mod h1:3dFJwm2MtCb312eHdHnK/w8D1lwgCeewa/2hztw89kE= |
60 | code.crute.us/mcrute/golib/echo v0.9.3 h1:rg14mTarLpRrpHPJN5X1k1VTFplo3rgOPCXPRf3xzmY= | 62 | code.crute.us/mcrute/golib/echo v0.9.3 h1:rg14mTarLpRrpHPJN5X1k1VTFplo3rgOPCXPRf3xzmY= |
61 | code.crute.us/mcrute/golib/echo v0.9.3/go.mod h1:mcmhqsSWD/+ECdrd0Sh9u9XGtukXdLPVHc88sKg/gJo= | 63 | code.crute.us/mcrute/golib/echo v0.9.3/go.mod h1:mcmhqsSWD/+ECdrd0Sh9u9XGtukXdLPVHc88sKg/gJo= |
62 | code.crute.us/mcrute/golib/secrets v0.2.0 h1:ENPZ+GEkdcmbFak0kHrI9JdSmVmBHxdzoKnjgvPHf5E= | ||
63 | code.crute.us/mcrute/golib/secrets v0.2.0/go.mod h1:O1ypm8JirXI4SekwNCHwQbfsieDQJxeRNwZYoot6fvw= | ||
64 | code.crute.us/mcrute/golib/vault v0.2.4 h1:lNc1hq26e/UAGBqxQlZiFffOXZSNEcEkKUzU3oRJ8Eg= | 64 | code.crute.us/mcrute/golib/vault v0.2.4 h1:lNc1hq26e/UAGBqxQlZiFffOXZSNEcEkKUzU3oRJ8Eg= |
65 | code.crute.us/mcrute/golib/vault v0.2.4/go.mod h1:23C5g8O0zaeFfo7v6sCO0RKgnHIiHM9ku+ASOWHJD9k= | 65 | code.crute.us/mcrute/golib/vault v0.2.4/go.mod h1:23C5g8O0zaeFfo7v6sCO0RKgnHIiHM9ku+ASOWHJD9k= |
66 | dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU= | 66 | dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU= |
@@ -515,8 +515,8 @@ github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UV | |||
515 | github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= | 515 | github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= |
516 | github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA= | 516 | github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA= |
517 | github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= | 517 | github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= |
518 | github.com/stretchr/testify v1.7.0 h1:nwc3DEeHmmLAfoZucVR881uASk0Mfjw8xYJ99tb5CcY= | ||
519 | github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= | 518 | github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= |
519 | github.com/stretchr/testify v1.8.1 h1:w7B6lhMri9wdJUVmEZPGGhZzrYTPvgJArz7wNPgYKsk= | ||
520 | github.com/subosito/gotenv v1.2.0/go.mod h1:N0PQaV/YGNqwC0u51sEeR/aUtSLEXKX9iv69rRypqCw= | 520 | github.com/subosito/gotenv v1.2.0/go.mod h1:N0PQaV/YGNqwC0u51sEeR/aUtSLEXKX9iv69rRypqCw= |
521 | github.com/tidwall/pretty v1.0.0 h1:HsD+QiTn7sK6flMKIvNmpqz1qrpP3Ps6jOKIKMooyg4= | 521 | github.com/tidwall/pretty v1.0.0 h1:HsD+QiTn7sK6flMKIvNmpqz1qrpP3Ps6jOKIKMooyg4= |
522 | github.com/tidwall/pretty v1.0.0/go.mod h1:XNkn88O1ChpSDQmQeStsy+sBenx6DDtFZJxhVysOjyk= | 522 | github.com/tidwall/pretty v1.0.0/go.mod h1:XNkn88O1ChpSDQmQeStsy+sBenx6DDtFZJxhVysOjyk= |
@@ -989,6 +989,7 @@ google.golang.org/protobuf v1.27.1/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQ | |||
989 | gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw= | 989 | gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw= |
990 | gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= | 990 | gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= |
991 | gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= | 991 | gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= |
992 | gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15 h1:YR8cESwS4TdDjEe65xsg0ogRM/Nc3DYOhEAlW+xobZo= | ||
992 | gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= | 993 | gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= |
993 | gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI= | 994 | gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI= |
994 | gopkg.in/ini.v1 v1.66.2/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k= | 995 | gopkg.in/ini.v1 v1.66.2/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k= |
@@ -1004,8 +1005,8 @@ gopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= | |||
1004 | gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY= | 1005 | gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY= |
1005 | gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ= | 1006 | gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ= |
1006 | gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= | 1007 | gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= |
1007 | gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b h1:h8qDotaEPuJATrMmW04NCwg7v22aHH28wwpauUhK9Oo= | ||
1008 | gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= | 1008 | gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= |
1009 | gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= | ||
1009 | honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= | 1010 | honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= |
1010 | honnef.co/go/tools v0.0.0-20190106161140-3f1c8253044a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= | 1011 | honnef.co/go/tools v0.0.0-20190106161140-3f1c8253044a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= |
1011 | honnef.co/go/tools v0.0.0-20190418001031-e561f6794a2a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= | 1012 | honnef.co/go/tools v0.0.0-20190418001031-e561f6794a2a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= |