gitforge/routers/init.go
Mathieu Fenniak 4d29e18b84 [v15.0/forgejo] fix: compliance with [migrations].ALLOWED_DOMAINS, ...BLOCKED_DOMAINS, and ....ALLOW_LOCALNETWORKS for git & LFS ops (#13185)
Partial backport of #13129, excluding the breaking change of disabling git http redirect.  Changes included:

System administrators can determine which hosts can be accessed by Forgejo for git mirroring via the config settings `[migrations].ALLOWED_DOMAINS`, `...BLOCKED_DOMAINS`, and `....ALLOW_LOCALNETWORKS`.  However, there were edge cases where these settings were ineffective:

- Config entries were only checked when mirrors were initially configured, and not later when a mirror pull/push occurred.  DNS changes, or server configuration changes, could cause a previously safe URL to no longer be safe.  These settings are now re-checked on every mirror pull/push.
- When performing Git LFS synchronization, it was assumed that the git URL passing migration URL checks was sufficient protection.  Specifically for pull mirrors which allow the usage of "Advanced settings" -> "LFS endpoint" to configure a separate LFS endpoint, these settings were never enforced, and for all mirrors they would have been subject to not being rechecked if the DNS had changed since configured.  LFS mirroring now uses an HTTP client that enforces the `[migrations]` config settings.

### Tests for Go changes

- I added test coverage for Go changes...
  - [x] in their respective `*_test.go` for unit tests.
  - [x] in the `tests/integration` directory if it involves interactions with a live Forgejo server.
- I ran...
  - [x] `make pr-go` before pushing

### Documentation

- [ ] I created a pull request [to the documentation](https://codeberg.org/forgejo/docs) to explain to Forgejo users how to use this change.
- [x] I did not document these changes and I do not expect someone else to do it.

### Release notes

- [x] This change will be noticed by a Forgejo user or admin (feature, bug fix, performance, etc.). I suggest to include a release note for this change.
  - This backport should have it's own release notes, as it does not include all the changes as #13129.
- [ ] This change is not visible to a Forgejo user or admin (refactor, dependency upgrade, etc.). I think there is no need to add a release note for this change.

<!--start release-notes-assistant-->

## Release notes
<!--URL:https://codeberg.org/forgejo/forgejo-->
- Security bug fixes
  - [PR](https://codeberg.org/forgejo/forgejo/pulls/13185): <!--number 13185 --><!--line 0 --><!--description SW1wcm92ZWQgY29tcGxpYW5jZSB3aXRoIHRoZSBjb25maWcgc2V0dGluZ3MgYFttaWdyYXRpb25zXS5BTExPV0VEX0RPTUFJTlNgLCBgW21pZ3JhdGlvbnNdLkJMT0NLRURfRE9NQUlOU2AsIGFuZCBgW21pZ3JhdGlvbnNdLkFMTE9XX0xPQ0FMTkVUV09SS1NgLCB3aGljaCBjb250cm9sIHRoZSByZW1vdGVzIHRoYXQgRm9yZ2VqbyBjYW4gYWNjZXNzIGZvciBnaXQgJiBMRlMgbWlncmF0aW9uIGFuZCBtaXJyb3Jpbmcgb3BlcmF0aW9ucywgZml4aW5nIHRpbWUtb2YtY2hlY2sgdnMuIHRpbWUtb2YtdXNlIGlzc3VlIGFuZCBtaXNzaW5nIGNoZWNrcyBpbiBMRlMu-->Improved compliance with the config settings `[migrations].ALLOWED_DOMAINS`, `[migrations].BLOCKED_DOMAINS`, and `[migrations].ALLOW_LOCALNETWORKS`, which control the remotes that Forgejo can access for git & LFS migration and mirroring operations, fixing time-of-check vs. time-of-use issue and missing checks in LFS.<!--description-->
<!--end release-notes-assistant-->

Reviewed-on: https://codeberg.org/forgejo/forgejo/pulls/13185
Reviewed-by: Andreas Ahlenstorf <aahlenst@noreply.codeberg.org>
2026-06-27 16:16:31 +02:00

209 lines
6.1 KiB
Go

// Copyright 2016 The Gitea Authors. All rights reserved.
// SPDX-License-Identifier: MIT
package routers
import (
"context"
"reflect"
"runtime"
"forgejo.org/models"
auth_model "forgejo.org/models/auth"
"forgejo.org/modules/cache"
"forgejo.org/modules/eventsource"
"forgejo.org/modules/git"
"forgejo.org/modules/highlight"
"forgejo.org/modules/log"
"forgejo.org/modules/markup"
"forgejo.org/modules/markup/external"
"forgejo.org/modules/setting"
"forgejo.org/modules/ssh"
"forgejo.org/modules/storage"
"forgejo.org/modules/svg"
"forgejo.org/modules/system"
"forgejo.org/modules/templates"
"forgejo.org/modules/translation"
"forgejo.org/modules/web"
actions_router "forgejo.org/routers/api/actions"
forgejo "forgejo.org/routers/api/forgejo/v1"
packages_router "forgejo.org/routers/api/packages"
apiv1 "forgejo.org/routers/api/v1"
"forgejo.org/routers/common"
"forgejo.org/routers/private"
web_routers "forgejo.org/routers/web"
actions_service "forgejo.org/services/actions"
auth_method "forgejo.org/services/auth/method"
"forgejo.org/services/auth/source/oauth2"
"forgejo.org/services/automerge"
"forgejo.org/services/cron"
federation_service "forgejo.org/services/federation"
feed_service "forgejo.org/services/feed"
indexer_service "forgejo.org/services/indexer"
"forgejo.org/services/mailer"
mailer_incoming "forgejo.org/services/mailer/incoming"
markup_service "forgejo.org/services/markup"
migrations_allowlist "forgejo.org/services/migrations/allowlist"
mirror_service "forgejo.org/services/mirror"
pull_service "forgejo.org/services/pull"
release_service "forgejo.org/services/release"
repo_service "forgejo.org/services/repository"
"forgejo.org/services/repository/archiver"
"forgejo.org/services/stats"
"forgejo.org/services/task"
"forgejo.org/services/uinotification"
"forgejo.org/services/webhook"
)
func mustInit(fn func() error) {
err := fn()
if err != nil {
ptr := reflect.ValueOf(fn).Pointer()
fi := runtime.FuncForPC(ptr)
log.Fatal("%s failed: %v", fi.Name(), err)
}
}
func mustInitCtx(ctx context.Context, fn func(ctx context.Context) error) {
err := fn(ctx)
if err != nil {
ptr := reflect.ValueOf(fn).Pointer()
fi := runtime.FuncForPC(ptr)
log.Fatal("%s(ctx) failed: %v", fi.Name(), err)
}
}
func syncAppConfForGit(ctx context.Context) error {
runtimeState := new(system.RuntimeState)
if err := system.AppState.Get(ctx, runtimeState); err != nil {
return err
}
updated := false
if runtimeState.LastAppPath != setting.AppPath {
log.Info("AppPath changed from '%s' to '%s'", runtimeState.LastAppPath, setting.AppPath)
runtimeState.LastAppPath = setting.AppPath
updated = true
}
if runtimeState.LastCustomConf != setting.CustomConf {
log.Info("CustomConf changed from '%s' to '%s'", runtimeState.LastCustomConf, setting.CustomConf)
runtimeState.LastCustomConf = setting.CustomConf
updated = true
}
if updated {
log.Info("re-sync repository hooks ...")
mustInitCtx(ctx, repo_service.SyncRepositoryHooks)
return system.AppState.Set(ctx, runtimeState)
}
return nil
}
func InitWebInstallPage(ctx context.Context) {
translation.InitLocales(ctx)
setting.LoadSettingsForInstall()
mustInit(svg.Init)
}
// InitWebInstalled is for global installed configuration.
func InitWebInstalled(ctx context.Context) {
mustInitCtx(ctx, git.InitFull)
log.Info("Git version: %s (home: %s)", git.VersionInfo(), git.HomeDir())
// Setup i18n
translation.InitLocales(ctx)
setting.LoadSettings()
mustInit(storage.Init)
mailer.NewContext(ctx)
mustInit(cache.Init)
mustInit(feed_service.Init)
mustInit(federation_service.Init)
mustInit(uinotification.Init)
mustInitCtx(ctx, archiver.Init)
highlight.NewContext()
external.RegisterRenderers()
markup.Init(markup_service.ProcessorHelper())
if setting.EnableSQLite3 {
log.Info("SQLite3 support is enabled")
} else if setting.Database.Type.IsSQLite3() {
log.Fatal("SQLite3 support is disabled, but it is used for database setting. Please get or build a Forgejo release with SQLite3 support.")
}
mustInitCtx(ctx, common.InitDBEngine)
log.Info("ORM engine initialization successful!")
mustInit(system.Init)
mustInitCtx(ctx, oauth2.Init)
mustInit(release_service.Init)
mustInitCtx(ctx, models.Init)
mustInitCtx(ctx, auth_model.Init)
mustInitCtx(ctx, repo_service.Init)
// Booting long running goroutines.
mustInit(indexer_service.Init)
mirror_service.InitSyncMirrors()
mustInit(webhook.Init)
mustInit(pull_service.Init)
mustInit(automerge.Init)
mustInit(task.Init)
mustInit(migrations_allowlist.Init)
eventsource.GetManager().Init()
mustInitCtx(ctx, mailer_incoming.Init)
mustInitCtx(ctx, syncAppConfForGit)
mustInitCtx(ctx, ssh.Init)
auth_method.Init()
mustInit(svg.Init)
actions_service.Init()
mustInit(stats.Init)
mustInit(actions_router.InitOIDC)
// Finally start up the cron
cron.NewContext(ctx)
}
// NormalRoutes represents non install routes
func NormalRoutes() *web.Route {
_ = templates.HTMLRenderer()
r := web.NewRoute()
r.Use(common.ProtocolMiddlewares()...)
r.Mount("/", web_routers.Routes())
r.Mount("/api/v1", apiv1.Routes())
r.Mount("/api/forgejo/v1", forgejo.Routes())
r.Mount("/api/internal", private.Routes())
r.Post("/-/fetch-redirect", common.FetchRedirectDelegate)
if setting.Packages.Enabled {
// This implements package support for most package managers
r.Mount("/api/packages", packages_router.CommonRoutes())
// This implements the OCI API (Note this is not preceded by /api but is instead /v2)
r.Mount("/v2", packages_router.ContainerRoutes())
}
if setting.Actions.Enabled {
prefix := "/api/actions"
r.Mount(prefix, actions_router.Routes(prefix))
// TODO: Pipeline api used for runner internal communication with gitea server. but only artifact is used for now.
// In Github, it uses ACTIONS_RUNTIME_URL=https://pipelines.actions.githubusercontent.com/fLgcSHkPGySXeIFrg8W8OBSfeg3b5Fls1A1CwX566g8PayEGlg/
// TODO: this prefix should be generated with a token string with runner ?
prefix = "/api/actions_pipeline"
r.Mount(prefix, actions_router.ArtifactsRoutes(prefix))
prefix = actions_router.ArtifactV4RouteBase
r.Mount(prefix, actions_router.ArtifactsV4Routes(prefix))
}
return r
}