mirror of
https://github.com/go-gitea/gitea.git
synced 2026-10-01 20:59:45 +09:00
1b1274486c
Introduces gitproxy module which spawns a small forward proxy as scanner for git calls Replaces hostmatcher with matchlist which supports port rules Deprecates ALLOWED_DOMAINS/BLOCKED_DOMAINS and ALLOW_LOCALNETWORKS settings in migration in favor of full names we have in security configs. Removes `external` preset in favor of lax/strict modes, strict mode requiring explicit ports if they aren't standard http/s ones. Breaking changes: - `external` preset no longer works as deny rule. To enforce that, use `strict` mode and allow ranges to connect to - Wildcards are no longer accepted in IP addresses - `*` is no longer allowed as entry in lists - domain rules now use curl like syntax `*.example.com` matching subdomains but not `example.com`, `example.com` matching itself and all subdomains. `example.*` is not a valid rule - In the default `lax` mode, `[security] ALLOWED_HOST_LIST` no longer restricts public hosts, set `EGRESS_MODE = strict` to keep an exclusive list. A startup warning flags this - Invalid list entries are logged at startup, invalid `BLOCKED_HOST_LIST`/`BLOCKED_DOMAINS` entries stop it Docs: https://gitea.com/gitea/docs/pulls/557 Signed-off-by: wxiaoguang <wxiaoguang@gmail.com> Co-authored-by: silverwind <me@silverwind.io> Co-authored-by: bircni <bircni@icloud.com> Co-authored-by: wxiaoguang <wxiaoguang@gmail.com>
101 lines
3.8 KiB
Go
101 lines
3.8 KiB
Go
// Copyright 2026 The Gitea Authors. All rights reserved.
|
|
// SPDX-License-Identifier: MIT
|
|
|
|
package egress
|
|
|
|
import (
|
|
"net/http"
|
|
"net/url"
|
|
"testing"
|
|
|
|
"gitea.dev/modules/setting"
|
|
"gitea.dev/modules/test"
|
|
|
|
"github.com/stretchr/testify/assert"
|
|
"github.com/stretchr/testify/require"
|
|
)
|
|
|
|
func mustURL(t *testing.T, raw string) *url.URL {
|
|
t.Helper()
|
|
u, err := url.Parse(raw)
|
|
require.NoError(t, err)
|
|
return u
|
|
}
|
|
|
|
func TestNewMigrationPolicy(t *testing.T) {
|
|
defer test.MockVariableValue(&setting.Migrations)()
|
|
for _, tc := range []struct {
|
|
allow, block, mode, target string
|
|
want bool
|
|
}{
|
|
{target: "https://1.2.3.4", want: true},
|
|
{allow: "github.com", target: "https://10.0.0.1"}, // a hostname allow doesn't cover a private IP
|
|
{allow: "github.com", target: "https://8.8.8.8", want: true}, // lax exempts public targets
|
|
{allow: "github.com", mode: "strict", target: "https://8.8.8.8"},
|
|
{block: "8.8.0.0/16", target: "https://8.8.8.8"},
|
|
} {
|
|
setting.Migrations.AllowedHostList, setting.Migrations.BlockedHostList, setting.Migrations.EgressMode = tc.allow, tc.block, tc.mode
|
|
u, err := url.Parse(tc.target)
|
|
require.NoError(t, err)
|
|
err = NewMigrationPolicy().CheckHostIPs(u)
|
|
assert.Equal(t, tc.want, err == nil, "%+v: %v", tc, err)
|
|
}
|
|
}
|
|
|
|
func TestWebhookPolicyProxy(t *testing.T) {
|
|
proxyURL := &url.URL{Scheme: "http", Host: "localhost:8080"}
|
|
defer test.MockVariableValue(&setting.Security.EgressMode, "lax")()
|
|
defer test.MockVariableValue(&setting.Webhook.AllowedHostList, "discordapp.com,s.discordapp.com")()
|
|
defer test.MockVariableValue(&setting.Webhook.ProxyURL, proxyURL.String())()
|
|
defer test.MockVariableValue(&setting.Webhook.ProxyURLFixed, proxyURL)()
|
|
defer test.MockVariableValue(&setting.Webhook.ProxyHosts, []string{"*.discordapp.com", "discordapp.com"})()
|
|
selectProxy := NewWebhookPolicy().NewHTTPTransport().Proxy
|
|
|
|
for target, want := range map[string]string{
|
|
"https://discordapp.com/api/webhooks/xxxxxxxxx/xxxxxxxxxxxxxxxxxxx": proxyURL.String(),
|
|
"http://s.discordapp.com/assets/xxxxxx": proxyURL.String(),
|
|
"http://github.com/a/b": "",
|
|
"http://www.discordapp.com/assets/xxxxxx": proxyURL.String(),
|
|
} {
|
|
req, err := http.NewRequest(http.MethodPost, target, nil)
|
|
require.NoError(t, err)
|
|
req.Host = ""
|
|
u, err := selectProxy(req)
|
|
require.NoError(t, err, target)
|
|
if want == "" {
|
|
assert.Nil(t, u, target)
|
|
} else {
|
|
assert.Equal(t, want, u.String(), target)
|
|
}
|
|
}
|
|
}
|
|
|
|
func TestSecurityPolicy(t *testing.T) {
|
|
defer test.MockVariableValue(&setting.Security.AllowedHostList, "avatars.example.com")()
|
|
defer test.MockVariableValue(&setting.Security.EgressMode, "lax")()
|
|
lax := NewSecurityPolicy("test")
|
|
assert.NoError(t, lax.CheckHost(mustURL(t, "https://avatars.example.com")))
|
|
assert.NoError(t, lax.CheckHost(mustURL(t, "https://8.8.8.8"))) // lax exempts public targets
|
|
assert.Error(t, lax.CheckHost(mustURL(t, "https://10.0.0.1"))) // restricted targets still need an allow entry
|
|
|
|
setting.Security.EgressMode = "strict"
|
|
strict := NewSecurityPolicy("test")
|
|
assert.NoError(t, strict.CheckHost(mustURL(t, "https://avatars.example.com")))
|
|
assert.Error(t, strict.CheckHost(mustURL(t, "https://8.8.8.8")))
|
|
}
|
|
|
|
func TestNewGitPolicy(t *testing.T) {
|
|
gitConfig := map[string]string{"http.proxy": "proxy.corp"}
|
|
defer test.MockVariableValue(&setting.GitConfig.Options, gitConfig)()
|
|
gitPolicy, err := NewGitPolicy()
|
|
require.NoError(t, err)
|
|
selected, err := gitPolicy.Proxy(&http.Request{URL: &url.URL{Scheme: "https", Host: "git.example.com"}})
|
|
require.NoError(t, err)
|
|
assert.Equal(t, "http://proxy.corp:1080", selected.String())
|
|
|
|
gitConfig["http.proxy"] = "http://user:secret@[::1"
|
|
_, err = NewGitPolicy()
|
|
require.Error(t, err)
|
|
assert.NotContains(t, err.Error(), "secret")
|
|
}
|