mirror of
https://github.com/go-gitea/gitea.git
synced 2026-10-21 09:06:46 +09:00
Introduce second list of addresses which are classified as dialable if explicitly allowed when in Lax mode. Restricted pool now includes: link-local, site local, private (including ULA), CGNAT, discard, dummy, documentation and test addreses. Reserved pool shrinks to: this network, wireserver embedding/translation ranges and multicasts Rationale for the choice is that while items in restricted pool can be dangerous to allow they could be a legitimate target in some deployments. Ranges left in reserved list are ranges which make no sense to dial, are public (wireserver) or are 6to4 embedding which cannot be reasonably verified to be safe. To unlock those a proxy should be used instead fixes: https://github.com/go-gitea/gitea/issues/39557 --------- Signed-off-by: TheFox0x7 <thefox0x7@gmail.com> Co-authored-by: wxiaoguang <wxiaoguang@gmail.com>
120 lines
3.7 KiB
Go
120 lines
3.7 KiB
Go
// Copyright 2026 The Gitea Authors. All rights reserved.
|
|
// SPDX-License-Identifier: MIT
|
|
|
|
package egress
|
|
|
|
import (
|
|
"cmp"
|
|
"errors"
|
|
"fmt"
|
|
"net"
|
|
"net/http"
|
|
"net/url"
|
|
"os"
|
|
"strings"
|
|
|
|
"gitea.dev/modules/egress/policy"
|
|
"gitea.dev/modules/proxy"
|
|
"gitea.dev/modules/setting"
|
|
|
|
"golang.org/x/net/http/httpproxy"
|
|
)
|
|
|
|
func NewMigrationPolicy() *policy.Policy {
|
|
return newMigrationPolicy(proxy.Proxy())
|
|
}
|
|
|
|
// NewGitPolicy is the migration policy for the git proxy, which keeps git's own proxy choice
|
|
func NewGitPolicy() (*policy.Policy, error) {
|
|
selectProxy, err := gitProxySelector()
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
return newMigrationPolicy(selectProxy), nil
|
|
}
|
|
|
|
// gitProxySelector picks proxies like git did: [git.config] http.proxy, else a [proxy] PROXY_URL, else the environment incl. ALL_PROXY
|
|
func gitProxySelector() (func(*http.Request) (*url.URL, error), error) {
|
|
env := httpproxy.FromEnvironment()
|
|
if rawURL, ok := setting.GitConfig.Options["http.proxy"]; ok {
|
|
gitProxy, err := normalizeGitProxy(rawURL)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("[git.config] http.proxy: %w", err)
|
|
}
|
|
env.HTTPProxy, env.HTTPSProxy = gitProxy, gitProxy
|
|
return requestProxy(env), nil
|
|
}
|
|
if setting.Proxy.Enabled && setting.Proxy.ProxyURL != "" {
|
|
return proxy.Proxy(), nil
|
|
}
|
|
allProxy := cmp.Or(os.Getenv("all_proxy"), os.Getenv("ALL_PROXY"))
|
|
env.HTTPProxy, env.HTTPSProxy = cmp.Or(env.HTTPProxy, allProxy), cmp.Or(env.HTTPSProxy, allProxy)
|
|
return requestProxy(env), nil
|
|
}
|
|
|
|
func requestProxy(cfg *httpproxy.Config) func(*http.Request) (*url.URL, error) {
|
|
proxyFunc := cfg.ProxyFunc()
|
|
return func(req *http.Request) (*url.URL, error) { return proxyFunc(req.URL) }
|
|
}
|
|
|
|
// normalizeGitProxy reads a proxy URL the way git reads http.proxy: http is the default scheme, 1080 curl's default port
|
|
func normalizeGitProxy(rawURL string) (string, error) {
|
|
if rawURL == "" {
|
|
return "", nil
|
|
}
|
|
if !strings.Contains(rawURL, "://") {
|
|
rawURL = "http://" + rawURL
|
|
}
|
|
proxyURL, err := url.Parse(rawURL)
|
|
if err != nil {
|
|
return "", errors.New("invalid URL") // the parse error would echo its credentials
|
|
}
|
|
if proxyURL.Scheme == "http" && proxyURL.Port() == "" {
|
|
proxyURL.Host = net.JoinHostPort(proxyURL.Hostname(), "1080")
|
|
}
|
|
return proxyURL.String(), nil
|
|
}
|
|
|
|
func newMigrationPolicy(selectProxy func(*http.Request) (*url.URL, error)) *policy.Policy {
|
|
return policy.NewPolicy("migrations", policyMode(setting.Migrations.EgressMode),
|
|
policy.WithAllow(setting.Migrations.AllowedHostList, "migrations.ALLOWED_HOST_LIST"),
|
|
policy.WithBlock(setting.Migrations.BlockedHostList, "migrations.BLOCKED_HOST_LIST"),
|
|
policy.WithLocalNeedsIPAllow(),
|
|
policy.WithProxy(selectProxy))
|
|
}
|
|
|
|
func NewWebhookPolicy() *policy.Policy {
|
|
var p *policy.Policy
|
|
selectProxy := proxy.WebHookProxy()
|
|
if webhookProxy := setting.Webhook.ProxyURLFixed; webhookProxy != nil {
|
|
next := selectProxy
|
|
selectProxy = func(req *http.Request) (*url.URL, error) {
|
|
u, err := next(req)
|
|
if err == nil && u == webhookProxy {
|
|
err = p.CheckHost(req.URL) // the webhook proxy resolves the target, so only its name can be checked
|
|
}
|
|
return u, err
|
|
}
|
|
}
|
|
p = policy.NewPolicy("webhook", policyMode(setting.Security.EgressMode),
|
|
policy.WithAllow(setting.Webhook.AllowedHostList, "security.ALLOWED_HOST_LIST"),
|
|
policy.WithLocalNeedsIPAllow(),
|
|
policy.WithProxy(selectProxy))
|
|
|
|
return p
|
|
}
|
|
|
|
func NewSecurityPolicy(usage string) *policy.Policy {
|
|
return policy.NewPolicy(usage, policyMode(setting.Security.EgressMode),
|
|
policy.WithAllow(setting.Security.AllowedHostList, "security.ALLOWED_HOST_LIST"),
|
|
policy.WithLocalNeedsIPAllow(),
|
|
policy.WithProxy(proxy.Proxy()))
|
|
}
|
|
|
|
func policyMode(mode string) policy.Mode {
|
|
if mode == "strict" {
|
|
return policy.Strict
|
|
}
|
|
return policy.Lax
|
|
}
|