aboutsummaryrefslogtreecommitdiff
path: root/app
diff options
context:
space:
mode:
authorMike Crute <mike@crute.us>2022-12-24 08:30:27 -0800
committerMike Crute <mike@crute.us>2022-12-24 08:30:27 -0800
commit4aac2b6026d27d4eba660b674bdb1f34bcfbfc54 (patch)
tree62eb040afb6d7f56f9bfb0af69cfc17ec2060a8b /app
parented1504c2826f6a5d406dd72e51f5a90b77ffea45 (diff)
downloadcloud-identity-broker-4aac2b6026d27d4eba660b674bdb1f34bcfbfc54.tar.bz2
cloud-identity-broker-4aac2b6026d27d4eba660b674bdb1f34bcfbfc54.tar.xz
cloud-identity-broker-4aac2b6026d27d4eba660b674bdb1f34bcfbfc54.zip
Use Vault for IAM users
Diffstat (limited to 'app')
-rw-r--r--app/controllers/api_account.go8
-rw-r--r--app/controllers/aws.go51
-rw-r--r--app/models/account.go12
-rw-r--r--app/models/session_key.go2
4 files changed, 59 insertions, 14 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
3import ( 3import (
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
3import ( 3import (
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.
22type AWSAPI struct { 25type AWSAPI struct {
23 Store models.AccountStore 26 Store models.AccountStore
27 Secrets secrets.Client
28 cache sync.Map // of aws.AWSClient
29}
30
31func (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.
55func (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 {
43type MongoDbAccountStore struct { 44type 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
58func GenerateSessionKey(ttl time.Duration) (*SessionKey, error) { 58func GenerateSessionKey(ttl time.Duration) (*SessionKey, error) {