go.chromium.org/luci@v0.0.0-20240309015107-7cdc2e660f33/auth_service/services/backend/main.go (about) 1 // Copyright 2021 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 // Package main is the main point of entry for the backend module. 16 // 17 // It handles task queue tasks and cron jobs. 18 package main 19 20 // Disable linting for imports, because of the dependency on config 21 // validation globals. 22 //nolint:all 23 import ( 24 "context" 25 "fmt" 26 "time" 27 28 "go.chromium.org/luci/auth_service/impl" 29 "go.chromium.org/luci/auth_service/impl/model" 30 31 // Ensure registration of validation rules. 32 // NOTE: this must go before anything that depends on validation globals, 33 // e.g. cfgcache.Register in srvcfg files in allowlistcfg/ or oauthcfg/. 34 "go.chromium.org/luci/auth_service/internal/configs/srvcfg/allowlistcfg" 35 "go.chromium.org/luci/auth_service/internal/configs/srvcfg/oauthcfg" 36 "go.chromium.org/luci/auth_service/internal/configs/srvcfg/permissionscfg" 37 "go.chromium.org/luci/auth_service/internal/configs/srvcfg/securitycfg" 38 "go.chromium.org/luci/auth_service/internal/configs/srvcfg/settingscfg" 39 "go.chromium.org/luci/auth_service/internal/configs/validation" 40 41 "go.chromium.org/luci/auth_service/internal/permissions" 42 "go.chromium.org/luci/auth_service/internal/pubsub" 43 "go.chromium.org/luci/auth_service/internal/realmsinternals" 44 45 "go.chromium.org/luci/common/errors" 46 "go.chromium.org/luci/common/logging" 47 "go.chromium.org/luci/server" 48 "go.chromium.org/luci/server/cron" 49 "go.chromium.org/luci/server/module" 50 ) 51 52 func main() { 53 modules := []module.Module{ 54 cron.NewModuleFromFlags(), 55 } 56 57 // Parse flags from environment variables. 58 dryRunCronConfig := model.ParseDryRunEnvVar(model.DryRunCronConfigEnvVar) 59 dryRunCronRealms := model.ParseDryRunEnvVar(model.DryRunCronRealmsEnvVar) 60 dryRunCronStaleAuth := model.ParseDryRunEnvVar(model.DryRunCronStaleAuthEnvVar) 61 62 impl.Main(modules, func(srv *server.Server) error { 63 cron.RegisterHandler("update-config", func(ctx context.Context) error { 64 historicalComment := "Updated from update-config cron" 65 66 // ip_allowlist.cfg handling. 67 if err := allowlistcfg.Update(ctx); err != nil { 68 return err 69 } 70 cfg, err := allowlistcfg.Get(ctx) 71 if err != nil { 72 return err 73 } 74 subnets, err := validation.GetSubnets(cfg.IpAllowlists) 75 if err != nil { 76 return err 77 } 78 if err := model.UpdateAllowlistEntities(ctx, subnets, dryRunCronConfig, historicalComment); err != nil { 79 return err 80 } 81 82 // oauth.cfg handling. 83 if err := oauthcfg.Update(ctx); err != nil { 84 return err 85 } 86 oauthcfg, err := oauthcfg.Get(ctx) 87 if err != nil { 88 return err 89 } 90 91 // security.cfg handling. 92 if err := securitycfg.Update(ctx); err != nil { 93 return err 94 } 95 securitycfg, err := securitycfg.Get(ctx) 96 if err != nil { 97 return err 98 } 99 100 if err := model.UpdateAuthGlobalConfig(ctx, oauthcfg, securitycfg, dryRunCronConfig, historicalComment); err != nil { 101 return err 102 } 103 104 // settings.cfg handling 105 if err := settingscfg.Update(ctx); err != nil { 106 return err 107 } 108 109 return nil 110 }) 111 112 cron.RegisterHandler("update-realms", func(ctx context.Context) error { 113 historicalComment := "Updated from update-realms cron" 114 115 // permissions.cfg handling. 116 if err := permissionscfg.Update(ctx); err != nil { 117 return err 118 } 119 permsCfg, permsMeta, err := permissionscfg.GetWithMetadata(ctx) 120 if err != nil { 121 return err 122 } 123 if err := model.UpdateAuthRealmsGlobals(ctx, permsCfg, dryRunCronRealms, historicalComment); err != nil { 124 return err 125 } 126 127 // Make the PermissionsDB for realms expansion. 128 permsDB := permissions.NewPermissionsDB(permsCfg, permsMeta) 129 130 // realms.cfg handling. 131 latestRealms, storedRealms, err := realmsinternals.GetConfigs(ctx) 132 if err != nil { 133 logging.Errorf(ctx, "aborting realms update - failed to fetch latest for all configs: %v", err) 134 return err 135 } 136 jobs, err := realmsinternals.CheckConfigChanges(ctx, permsDB, latestRealms, storedRealms, dryRunCronRealms, historicalComment) 137 if err != nil { 138 return err 139 } 140 if !executeJobs(ctx, jobs, 2*time.Second) { 141 return fmt.Errorf("not all jobs succeeded when refreshing realms") 142 } 143 144 return nil 145 }) 146 147 cron.RegisterHandler("revoke-stale-authorization", func(ctx context.Context) error { 148 // Only members of the below trusted group are eligible to: 149 // * be authorized to subscribe to PubSub notifications of AuthDB changes 150 // * be authorized to read the AuthDB from Google Storage. 151 // This cron revokes all stale authorizations for accounts that are no 152 // longer in the trusted group. 153 trustedGroup := model.TrustedServicesGroup 154 155 if err := pubsub.RevokeStaleAuthorization(ctx, trustedGroup, dryRunCronStaleAuth); err != nil { 156 err = errors.Annotate(err, "error revoking stale PubSub authorizations").Err() 157 logging.Errorf(ctx, err.Error()) 158 return err 159 } 160 161 if err := model.RevokeStaleReaderAccess(ctx, trustedGroup, dryRunCronStaleAuth); err != nil { 162 err = errors.Annotate(err, "error revoking stale AuthDB reader access").Err() 163 logging.Errorf(ctx, err.Error()) 164 return err 165 } 166 167 return nil 168 }) 169 170 // TODO: Remove comparison code once we have fully rolled out Auth 171 // Service v2 (b/321019030). 172 cron.RegisterHandler("auth-service-v2-validation", func(ctx context.Context) error { 173 logging.Infof(ctx, "starting comparison of V2 entities for validation") 174 return model.CompareV2Entities(ctx) 175 }) 176 177 return nil 178 }) 179 } 180 181 // executeJobs executes the callbacks, sleeping the set amount of time 182 // between each. Note: all callbacks will be run, even if a previous job 183 // returned an error. 184 // 185 // Returns whether any job returned an error. 186 func executeJobs(ctx context.Context, jobs []func() error, sleepTime time.Duration) bool { 187 success := true 188 for i, job := range jobs { 189 if i > 0 { 190 time.Sleep(sleepTime) 191 } 192 if err := job(); err != nil { 193 logging.Errorf(ctx, "job %d out of %d failed: %s", i+1, len(jobs), err) 194 success = false 195 } 196 } 197 return success 198 }