go.chromium.org/luci@v0.0.0-20240309015107-7cdc2e660f33/tokenserver/appengine/backend/main.go (about) 1 // Copyright 2017 The LUCI Authors. 2 // 3 // Licensed under the Apache License, Version 2.0 (the "License"); 4 // you may not use this file except in compliance with the License. 5 // You may obtain a copy of the License at 6 // 7 // http://www.apache.org/licenses/LICENSE-2.0 8 // 9 // Unless required by applicable law or agreed to in writing, software 10 // distributed under the License is distributed on an "AS IS" BASIS, 11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 // See the License for the specific language governing permissions and 13 // limitations under the License. 14 15 // Binary backend implements HTTP server that handles requests to 'backend' 16 // module. 17 package main 18 19 import ( 20 "context" 21 "fmt" 22 "sync" 23 24 "google.golang.org/protobuf/types/known/emptypb" 25 26 "go.chromium.org/luci/auth/identity" 27 "go.chromium.org/luci/common/errors" 28 "go.chromium.org/luci/common/logging" 29 "go.chromium.org/luci/grpc/grpcutil" 30 "go.chromium.org/luci/server" 31 "go.chromium.org/luci/server/auth" 32 "go.chromium.org/luci/server/auth/openid" 33 "go.chromium.org/luci/server/cron" 34 "go.chromium.org/luci/server/router" 35 36 "go.chromium.org/luci/tokenserver/api/admin/v1" 37 38 "go.chromium.org/luci/tokenserver/appengine/impl" 39 "go.chromium.org/luci/tokenserver/appengine/impl/utils/bq" 40 ) 41 42 func main() { 43 impl.Main(func(srv *server.Server, services *impl.Services) error { 44 bqInserter, err := bq.NewInserter(srv.Context, srv.Options.CloudProject) 45 if err != nil { 46 return err 47 } 48 49 // GAE crons. 50 cron.RegisterHandler("read-config", func(ctx context.Context) error { 51 return readConfigCron(ctx, services.Admin) 52 }) 53 cron.RegisterHandler("fetch-crl", func(ctx context.Context) error { 54 return fetchCRLCron(ctx, services.Certs) 55 }) 56 57 // PubSub push handler processing messages produced by impl/utils/bq. 58 oidcMW := router.NewMiddlewareChain( 59 auth.Authenticate(&openid.GoogleIDTokenAuthMethod{ 60 AudienceCheck: openid.AudienceMatchesHost, 61 }), 62 ) 63 // bigquery-log-pubsub@ is a part of the PubSub Push subscription config. 64 pusherID := identity.Identity(fmt.Sprintf("user:bigquery-log-pubsub@%s.iam.gserviceaccount.com", srv.Options.CloudProject)) 65 srv.Routes.POST("/internal/pubsub/bigquery-log", oidcMW, func(c *router.Context) { 66 if got := auth.CurrentIdentity(c.Request.Context()); got != pusherID { 67 logging.Errorf(c.Request.Context(), "Expecting ID token of %q, got %q", pusherID, got) 68 c.Writer.WriteHeader(403) 69 } else { 70 err := bqInserter.HandlePubSubPush(c.Request.Context(), c.Request.Body) 71 if err != nil { 72 logging.Errorf(c.Request.Context(), "Failed to process the message: %s", err) 73 c.Writer.WriteHeader(500) 74 } 75 } 76 }) 77 78 return nil 79 }) 80 } 81 82 // readConfigCron is a handler for the "read-config" cron. 83 func readConfigCron(ctx context.Context, adminServer admin.AdminServer) error { 84 // All config fetching callbacks to call in parallel. 85 fetchers := []struct { 86 name string 87 cb func(context.Context, *emptypb.Empty) (*admin.ImportedConfigs, error) 88 }{ 89 {"ImportCAConfigs", adminServer.ImportCAConfigs}, 90 {"ImportDelegationConfigs", adminServer.ImportDelegationConfigs}, 91 {"ImportProjectIdentityConfigs", adminServer.ImportProjectIdentityConfigs}, 92 {"ImportProjectOwnedAccountsConfigs", adminServer.ImportProjectOwnedAccountsConfigs}, 93 } 94 95 errs := errors.NewLazyMultiError(len(fetchers)) 96 97 wg := sync.WaitGroup{} 98 wg.Add(len(fetchers)) 99 for idx, fetcher := range fetchers { 100 idx := idx 101 fetcher := fetcher 102 go func() { 103 defer wg.Done() 104 if _, err := fetcher.cb(ctx, nil); err != nil { 105 logging.Errorf(ctx, "%s failed - %s", fetcher.name, err) 106 errs.Assign(idx, grpcutil.WrapIfTransient(err)) 107 } 108 }() 109 } 110 wg.Wait() 111 112 return errs.Get() 113 } 114 115 // fetchCRLCron is a handler for the "fetch-crl" cron. 116 func fetchCRLCron(ctx context.Context, caServer admin.CertificateAuthoritiesServer) error { 117 list, err := caServer.ListCAs(ctx, nil) 118 if err != nil { 119 return err 120 } 121 122 errs := errors.NewLazyMultiError(len(list.Cn)) 123 124 // Fetch CRL of each active CA in parallel. In practice there are very few 125 // CAs there (~= 1), so the risk of OOM is small. 126 wg := sync.WaitGroup{} 127 wg.Add(len(list.Cn)) 128 for idx, cn := range list.Cn { 129 idx := idx 130 cn := cn 131 go func() { 132 defer wg.Done() 133 if _, err := caServer.FetchCRL(ctx, &admin.FetchCRLRequest{Cn: cn}); err != nil { 134 logging.Errorf(ctx, "FetchCRL(%q) failed - %s", cn, err) 135 errs.Assign(idx, grpcutil.WrapIfTransient(err)) 136 } 137 }() 138 } 139 wg.Wait() 140 141 return errs.Get() 142 }