Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(152)

Side by Side Diff: server/auth/authdb/snapshot.go

Issue 2386643003: auth: Make luci-go services trust signatures produced by the token server. (Closed)
Patch Set: Created 4 years, 2 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
OLDNEW
1 // Copyright 2016 The LUCI Authors. All rights reserved. 1 // Copyright 2016 The LUCI Authors. All rights reserved.
2 // Use of this source code is governed under the Apache License, Version 2.0 2 // Use of this source code is governed under the Apache License, Version 2.0
3 // that can be found in the LICENSE file. 3 // that can be found in the LICENSE file.
4 4
5 package authdb 5 package authdb
6 6
7 import ( 7 import (
8 "fmt" 8 "fmt"
9 "net" 9 "net"
10 "strings" 10 "strings"
11 "sync"
12 "time"
11 13
12 "golang.org/x/net/context" 14 "golang.org/x/net/context"
13 15
16 "github.com/luci/luci-go/common/clock"
17 "github.com/luci/luci-go/common/data/caching/lazyslot"
14 "github.com/luci/luci-go/common/logging" 18 "github.com/luci/luci-go/common/logging"
15 "github.com/luci/luci-go/server/auth/identity" 19 "github.com/luci/luci-go/server/auth/identity"
16 "github.com/luci/luci-go/server/auth/service/protocol" 20 "github.com/luci/luci-go/server/auth/service/protocol"
21 "github.com/luci/luci-go/server/auth/signing"
17 "github.com/luci/luci-go/server/secrets" 22 "github.com/luci/luci-go/server/secrets"
18 ) 23 )
19 24
20 // OAuth client_id of https://apis-explorer.appspot.com/. 25 // OAuth client_id of https://apis-explorer.appspot.com/.
21 const googleAPIExplorerClientID = "292824132082.apps.googleusercontent.com" 26 const googleAPIExplorerClientID = "292824132082.apps.googleusercontent.com"
22 27
23 // SnapshotDB implements DB using AuthDB proto message. 28 // SnapshotDB implements DB using AuthDB proto message.
24 // 29 //
25 // Use NewSnapshotDB to create new instances. Don't touch public fields 30 // Use NewSnapshotDB to create new instances. Don't touch public fields
26 // of existing instances. 31 // of existing instances.
27 type SnapshotDB struct { 32 type SnapshotDB struct {
28 AuthServiceURL string // where it was fetched from 33 AuthServiceURL string // where it was fetched from
29 Rev int64 // its revision number 34 Rev int64 // its revision number
30 35
31 tokenServiceURL string // URL of the token server as provided by Auth se rvice 36 tokenServiceURL string // URL of the token server as provided by Auth se rvice
32 37
33 clientIDs map[string]struct{} // set of allowed client IDs 38 clientIDs map[string]struct{} // set of allowed client IDs
34 groups map[string]*group // map of all known groups 39 groups map[string]*group // map of all known groups
35 secrets secrets.StaticStore // secrets shared by all service with this DB 40 secrets secrets.StaticStore // secrets shared by all service with this DB
36 41
37 assignments map[identity.Identity]string // IP whitelist assignements 42 assignments map[identity.Identity]string // IP whitelist assignements
38 whitelists map[string][]net.IPNet // IP whitelists 43 whitelists map[string][]net.IPNet // IP whitelists
44
45 // Certs are loaded lazily in GetCertificates since they are used only w hen
46 // checking delegation tokens, which is relatively rare.
47 certs lazyslot.Slot
Vadim Sh. 2016/10/01 04:04:43 lazyslot.Slot takes care of initial lazy fetch and
39 } 48 }
40 49
50 var _ DB = &SnapshotDB{}
51
41 // group is a node in a group graph. Nested groups are referenced directly via 52 // group is a node in a group graph. Nested groups are referenced directly via
42 // pointer. 53 // pointer.
43 type group struct { 54 type group struct {
44 members map[identity.Identity]struct{} // set of all members 55 members map[identity.Identity]struct{} // set of all members
45 globs []identity.Glob // list of all identity globs 56 globs []identity.Glob // list of all identity globs
46 nested []*group // pointers to nested groups 57 nested []*group // pointers to nested groups
47 } 58 }
48 59
49 var _ DB = &SnapshotDB{} 60 // certMap is used in GetCertificate and fetchTrustedCerts.
61 type certMap map[identity.Identity]*signing.PublicCertificates
50 62
51 // NewSnapshotDB creates new instance of SnapshotDB. 63 // NewSnapshotDB creates new instance of SnapshotDB.
52 // 64 //
53 // It does some preprocessing to speed up subsequent checks. Return errors if 65 // It does some preprocessing to speed up subsequent checks. Return errors if
54 // it encounters inconsistencies. 66 // it encounters inconsistencies.
55 func NewSnapshotDB(authDB *protocol.AuthDB, authServiceURL string, rev int64) (* SnapshotDB, error) { 67 func NewSnapshotDB(authDB *protocol.AuthDB, authServiceURL string, rev int64) (* SnapshotDB, error) {
Vadim Sh. 2016/10/03 20:47:12 this is main prod implementation
56 db := &SnapshotDB{ 68 db := &SnapshotDB{
57 AuthServiceURL: authServiceURL, 69 AuthServiceURL: authServiceURL,
58 Rev: rev, 70 Rev: rev,
59 71
60 tokenServiceURL: authDB.GetTokenServerUrl(), 72 tokenServiceURL: authDB.GetTokenServerUrl(),
61 } 73 }
62 74
75 // Load all trusted certificates lazily and refresh each hour.
76 db.certs.Fetcher = func(c context.Context, prev lazyslot.Value) (lazyslo t.Value, error) {
77 mapping, err := db.fetchTrustedCerts(c)
78 if err != nil {
79 return lazyslot.Value{}, err
80 }
81 return lazyslot.Value{
82 Value: mapping,
83 Expiration: clock.Now(c).Add(time.Hour),
84 }, nil
85 }
86
63 // Set of all allowed clientIDs. 87 // Set of all allowed clientIDs.
64 db.clientIDs = make(map[string]struct{}, 2+len(authDB.GetOauthAdditional ClientIds())) 88 db.clientIDs = make(map[string]struct{}, 2+len(authDB.GetOauthAdditional ClientIds()))
65 db.clientIDs[googleAPIExplorerClientID] = struct{}{} 89 db.clientIDs[googleAPIExplorerClientID] = struct{}{}
66 if authDB.GetOauthClientId() != "" { 90 if authDB.GetOauthClientId() != "" {
67 db.clientIDs[authDB.GetOauthClientId()] = struct{}{} 91 db.clientIDs[authDB.GetOauthClientId()] = struct{}{}
68 } 92 }
69 for _, cid := range authDB.GetOauthAdditionalClientIds() { 93 for _, cid := range authDB.GetOauthAdditionalClientIds() {
70 if cid != "" { 94 if cid != "" {
71 db.clientIDs[cid] = struct{}{} 95 db.clientIDs[cid] = struct{}{}
72 } 96 }
(...skipping 178 matching lines...) Expand 10 before | Expand all | Expand 10 after
251 } 275 }
252 276
253 // SharedSecrets is secrets.Store with secrets in Auth DB. 277 // SharedSecrets is secrets.Store with secrets in Auth DB.
254 // 278 //
255 // Such secrets are usually generated on central Auth Service and are known 279 // Such secrets are usually generated on central Auth Service and are known
256 // to all trusted services (so that they can use them to exchange data). 280 // to all trusted services (so that they can use them to exchange data).
257 func (db *SnapshotDB) SharedSecrets(c context.Context) (secrets.Store, error) { 281 func (db *SnapshotDB) SharedSecrets(c context.Context) (secrets.Store, error) {
258 return db.secrets, nil 282 return db.secrets, nil
259 } 283 }
260 284
285 // GetCertificates returns a bundle with certificates of a trusted signer.
286 func (db *SnapshotDB) GetCertificates(c context.Context, signerID identity.Ident ity) (*signing.PublicCertificates, error) {
Vadim Sh. 2016/10/01 04:04:43 this is the main implementation of GetCertificates
M-A Ruel 2016/10/01 11:18:41 Which brings me: why so many mocks?
Vadim Sh. 2016/10/03 20:47:12 Replied in separate comments. They are all necessa
287 val, err := db.certs.Get(c)
288 if err != nil {
289 return nil, err
290 }
291 trustedCertsMap := val.Value.(certMap)
292 return trustedCertsMap[signerID], nil
M-A Ruel 2016/10/01 11:18:41 Humm this will panic if the signerID is missing in
Vadim Sh. 2016/10/03 20:47:12 No, it will return 'nil'. Maps don't panic, they r
293 }
294
261 // GetWhitelistForIdentity returns name of the IP whitelist to use to check 295 // GetWhitelistForIdentity returns name of the IP whitelist to use to check
262 // IP of requests from given `ident`. 296 // IP of requests from given `ident`.
263 // 297 //
264 // It's used to restrict access for certain account to certain IP subnets. 298 // It's used to restrict access for certain account to certain IP subnets.
265 // 299 //
266 // Returns ("", nil) if `ident` is not IP restricted. 300 // Returns ("", nil) if `ident` is not IP restricted.
267 func (db *SnapshotDB) GetWhitelistForIdentity(c context.Context, ident identity. Identity) (string, error) { 301 func (db *SnapshotDB) GetWhitelistForIdentity(c context.Context, ident identity. Identity) (string, error) {
268 return db.assignments[ident], nil 302 return db.assignments[ident], nil
269 } 303 }
270 304
(...skipping 17 matching lines...) Expand all
288 func (db *SnapshotDB) GetAuthServiceURL(c context.Context) (string, error) { 322 func (db *SnapshotDB) GetAuthServiceURL(c context.Context) (string, error) {
289 return db.AuthServiceURL, nil 323 return db.AuthServiceURL, nil
290 } 324 }
291 325
292 // GetTokenServiceURL returns root URL ("https://<host>") of the token server. 326 // GetTokenServiceURL returns root URL ("https://<host>") of the token server.
293 // 327 //
294 // This is needed to implement authdb.DB interface. 328 // This is needed to implement authdb.DB interface.
295 func (db *SnapshotDB) GetTokenServiceURL(c context.Context) (string, error) { 329 func (db *SnapshotDB) GetTokenServiceURL(c context.Context) (string, error) {
296 return db.tokenServiceURL, nil 330 return db.tokenServiceURL, nil
297 } 331 }
332
333 //// Implementation details.
334
335 // fetchTrustedCerts is called by db.certs to fetch certificates.
336 func (db *SnapshotDB) fetchTrustedCerts(c context.Context) (certMap, error) {
337 // TODO(vadimsh): Stop loading certs from AuthServiceURL when all tokens
338 // are generated by the token server.
339 var urls []string
340 if db.tokenServiceURL != "" {
341 urls = []string{db.tokenServiceURL, db.AuthServiceURL}
342 } else {
343 urls = []string{db.AuthServiceURL}
344 }
345
346 certs := make([]*signing.PublicCertificates, len(urls))
347 errs := make([]error, len(urls))
348
349 // Fetch in parallel.
M-A Ruel 2016/10/01 11:18:41 I'm surprised there isn't a package to do this yet
Vadim Sh. 2016/10/03 20:47:12 There probably is one, but I find it simpler to us
350 wg := sync.WaitGroup{}
351 for i, url := range urls {
352 i := i
M-A Ruel 2016/10/01 11:18:41 optional nit: I prefer passing flags on the functi
Vadim Sh. 2016/10/03 20:47:12 Done.
353 url := url
354 wg.Add(1)
355 go func() {
356 certs[i], errs[i] = signing.FetchCertificatesFromLUCISer vice(c, url)
357 wg.Done()
M-A Ruel 2016/10/01 11:18:41 optional nit: I generally prefer defer wg.Done() o
Vadim Sh. 2016/10/03 20:47:12 Done.
358 }()
359 }
360 wg.Wait()
361
362 // Each certificate bundle is available under two keys: "service:<app-id >" and
363 // "user:<service-account-name>". This is so because there's one to one
364 // association between GAE apps and corresponding @appspot.gserviceaccou nt.com
365 // service accounts.
366 mapping := make(certMap, 2*len(certs))
367 for i, cert := range certs {
368 if errs[i] != nil {
369 return nil, errs[i]
370 }
371 if cert.AppID != "" {
372 id, err := identity.MakeIdentity("service:" + cert.AppID )
373 if err != nil {
374 return nil, fmt.Errorf("invalid app_id %q in fet ched certificates bundle - %s", cert.AppID, err)
375 }
376 mapping[id] = cert
377 }
378 if cert.ServiceAccountName != "" {
379 id, err := identity.MakeIdentity("user:" + cert.ServiceA ccountName)
380 if err != nil {
381 return nil, fmt.Errorf("invalid service_account_ name %q in fetched certificates bundle - %s", cert.ServiceAccountName, err)
382 }
383 mapping[id] = cert
384 }
385 }
386
387 return mapping, nil
388 }
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698