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

Unified 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, 3 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 side-by-side diff with in-line comments
Download patch
Index: server/auth/authdb/snapshot.go
diff --git a/server/auth/authdb/snapshot.go b/server/auth/authdb/snapshot.go
index 0ede9ac2fc201e786d9ca53f7b7d171b95bd2ec6..4d763d7e23db71afd7d189c1c447b5d34c0182dd 100644
--- a/server/auth/authdb/snapshot.go
+++ b/server/auth/authdb/snapshot.go
@@ -8,12 +8,17 @@ import (
"fmt"
"net"
"strings"
+ "sync"
+ "time"
"golang.org/x/net/context"
+ "github.com/luci/luci-go/common/clock"
+ "github.com/luci/luci-go/common/data/caching/lazyslot"
"github.com/luci/luci-go/common/logging"
"github.com/luci/luci-go/server/auth/identity"
"github.com/luci/luci-go/server/auth/service/protocol"
+ "github.com/luci/luci-go/server/auth/signing"
"github.com/luci/luci-go/server/secrets"
)
@@ -36,8 +41,14 @@ type SnapshotDB struct {
assignments map[identity.Identity]string // IP whitelist assignements
whitelists map[string][]net.IPNet // IP whitelists
+
+ // Certs are loaded lazily in GetCertificates since they are used only when
+ // checking delegation tokens, which is relatively rare.
+ certs lazyslot.Slot
Vadim Sh. 2016/10/01 04:04:43 lazyslot.Slot takes care of initial lazy fetch and
}
+var _ DB = &SnapshotDB{}
+
// group is a node in a group graph. Nested groups are referenced directly via
// pointer.
type group struct {
@@ -46,7 +57,8 @@ type group struct {
nested []*group // pointers to nested groups
}
-var _ DB = &SnapshotDB{}
+// certMap is used in GetCertificate and fetchTrustedCerts.
+type certMap map[identity.Identity]*signing.PublicCertificates
// NewSnapshotDB creates new instance of SnapshotDB.
//
@@ -60,6 +72,18 @@ func NewSnapshotDB(authDB *protocol.AuthDB, authServiceURL string, rev int64) (*
tokenServiceURL: authDB.GetTokenServerUrl(),
}
+ // Load all trusted certificates lazily and refresh each hour.
+ db.certs.Fetcher = func(c context.Context, prev lazyslot.Value) (lazyslot.Value, error) {
+ mapping, err := db.fetchTrustedCerts(c)
+ if err != nil {
+ return lazyslot.Value{}, err
+ }
+ return lazyslot.Value{
+ Value: mapping,
+ Expiration: clock.Now(c).Add(time.Hour),
+ }, nil
+ }
+
// Set of all allowed clientIDs.
db.clientIDs = make(map[string]struct{}, 2+len(authDB.GetOauthAdditionalClientIds()))
db.clientIDs[googleAPIExplorerClientID] = struct{}{}
@@ -258,6 +282,16 @@ func (db *SnapshotDB) SharedSecrets(c context.Context) (secrets.Store, error) {
return db.secrets, nil
}
+// GetCertificates returns a bundle with certificates of a trusted signer.
+func (db *SnapshotDB) GetCertificates(c context.Context, signerID identity.Identity) (*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
+ val, err := db.certs.Get(c)
+ if err != nil {
+ return nil, err
+ }
+ trustedCertsMap := val.Value.(certMap)
+ 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
+}
+
// GetWhitelistForIdentity returns name of the IP whitelist to use to check
// IP of requests from given `ident`.
//
@@ -295,3 +329,60 @@ func (db *SnapshotDB) GetAuthServiceURL(c context.Context) (string, error) {
func (db *SnapshotDB) GetTokenServiceURL(c context.Context) (string, error) {
return db.tokenServiceURL, nil
}
+
+//// Implementation details.
+
+// fetchTrustedCerts is called by db.certs to fetch certificates.
+func (db *SnapshotDB) fetchTrustedCerts(c context.Context) (certMap, error) {
+ // TODO(vadimsh): Stop loading certs from AuthServiceURL when all tokens
+ // are generated by the token server.
+ var urls []string
+ if db.tokenServiceURL != "" {
+ urls = []string{db.tokenServiceURL, db.AuthServiceURL}
+ } else {
+ urls = []string{db.AuthServiceURL}
+ }
+
+ certs := make([]*signing.PublicCertificates, len(urls))
+ errs := make([]error, len(urls))
+
+ // 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
+ wg := sync.WaitGroup{}
+ for i, url := range urls {
+ 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.
+ url := url
+ wg.Add(1)
+ go func() {
+ certs[i], errs[i] = signing.FetchCertificatesFromLUCIService(c, url)
+ 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.
+ }()
+ }
+ wg.Wait()
+
+ // Each certificate bundle is available under two keys: "service:<app-id>" and
+ // "user:<service-account-name>". This is so because there's one to one
+ // association between GAE apps and corresponding @appspot.gserviceaccount.com
+ // service accounts.
+ mapping := make(certMap, 2*len(certs))
+ for i, cert := range certs {
+ if errs[i] != nil {
+ return nil, errs[i]
+ }
+ if cert.AppID != "" {
+ id, err := identity.MakeIdentity("service:" + cert.AppID)
+ if err != nil {
+ return nil, fmt.Errorf("invalid app_id %q in fetched certificates bundle - %s", cert.AppID, err)
+ }
+ mapping[id] = cert
+ }
+ if cert.ServiceAccountName != "" {
+ id, err := identity.MakeIdentity("user:" + cert.ServiceAccountName)
+ if err != nil {
+ return nil, fmt.Errorf("invalid service_account_name %q in fetched certificates bundle - %s", cert.ServiceAccountName, err)
+ }
+ mapping[id] = cert
+ }
+ }
+
+ return mapping, nil
+}

Powered by Google App Engine
This is Rietveld 408576698