Skip to content

Commit 55bdc9a

Browse files
lunnytechknowlogick
authored andcommitted
Webhook support custom proxy (#8760)
* Webhook support custom proxy * Add glob support on webhook proxy host rules * fix app.ini.sample * improve code and app.ini.sample * update cheetsheet about added webhook options
1 parent 016a5d0 commit 55bdc9a

File tree

6 files changed

+101
-3
lines changed

6 files changed

+101
-3
lines changed

custom/conf/app.ini.sample

+4
Original file line numberDiff line numberDiff line change
@@ -511,6 +511,10 @@ DELIVER_TIMEOUT = 5
511511
SKIP_TLS_VERIFY = false
512512
; Number of history information in each page
513513
PAGING_NUM = 10
514+
; Proxy server URL, support http://, https//, socks://, blank will follow environment http_proxy/https_proxy
515+
PROXY_URL =
516+
; Comma separated list of host names requiring proxy. Glob patterns (*) are accepted; use ** to match all hosts.
517+
PROXY_HOSTS =
514518

515519
[mailer]
516520
ENABLED = false

docs/content/doc/advanced/config-cheat-sheet.en-us.md

+2
Original file line numberDiff line numberDiff line change
@@ -312,6 +312,8 @@ relation to port exhaustion.
312312
- `DELIVER_TIMEOUT`: **5**: Delivery timeout (sec) for shooting webhooks.
313313
- `SKIP_TLS_VERIFY`: **false**: Allow insecure certification.
314314
- `PAGING_NUM`: **10**: Number of webhook history events that are shown in one page.
315+
- `PROXY_URL`: ****: Proxy server URL, support http://, https//, socks://, blank will follow environment http_proxy/https_proxy
316+
- `PROXY_HOSTS`: ****: Comma separated list of host names requiring proxy. Glob patterns (*) are accepted; use ** to match all hosts.
315317

316318
## Mailer (`mailer`)
317319

docs/content/doc/advanced/config-cheat-sheet.zh-cn.md

+2
Original file line numberDiff line numberDiff line change
@@ -129,6 +129,8 @@ menu:
129129
- `DELIVER_TIMEOUT`: 请求webhooks的超时时间,单位秒。
130130
- `SKIP_TLS_VERIFY`: 是否允许不安全的证书。
131131
- `PAGING_NUM`: 每页显示的Webhook 历史数量。
132+
- `PROXY_URL`: ****: 代理服务器网址,支持 http://, https//, socks://, 为空将使用环境变量中的 http_proxy/https_proxy 设置。
133+
- `PROXY_HOSTS`: ****: 逗号分隔的需要代理的域名或IP地址。支持 * 号匹配符,使用 ** 匹配所有域名和IP地址。
132134

133135
## Mailer (`mailer`)
134136

modules/setting/webhook.go

+21
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,12 @@
44

55
package setting
66

7+
import (
8+
"net/url"
9+
10+
"code.gitea.io/gitea/modules/log"
11+
)
12+
713
var (
814
// Webhook settings
915
Webhook = struct {
@@ -12,11 +18,16 @@ var (
1218
SkipTLSVerify bool
1319
Types []string
1420
PagingNum int
21+
ProxyURL string
22+
ProxyURLFixed *url.URL
23+
ProxyHosts []string
1524
}{
1625
QueueLength: 1000,
1726
DeliverTimeout: 5,
1827
SkipTLSVerify: false,
1928
PagingNum: 10,
29+
ProxyURL: "",
30+
ProxyHosts: []string{},
2031
}
2132
)
2233

@@ -27,4 +38,14 @@ func newWebhookService() {
2738
Webhook.SkipTLSVerify = sec.Key("SKIP_TLS_VERIFY").MustBool()
2839
Webhook.Types = []string{"gitea", "gogs", "slack", "discord", "dingtalk", "telegram", "msteams"}
2940
Webhook.PagingNum = sec.Key("PAGING_NUM").MustInt(10)
41+
Webhook.ProxyURL = sec.Key("PROXY_URL").MustString("")
42+
if Webhook.ProxyURL != "" {
43+
var err error
44+
Webhook.ProxyURLFixed, err = url.Parse(Webhook.ProxyURL)
45+
if err != nil {
46+
log.Error("Webhook PROXY_URL is not valid")
47+
Webhook.ProxyURL = ""
48+
}
49+
}
50+
Webhook.ProxyHosts = sec.Key("PROXY_HOSTS").Strings(",")
3051
}

modules/webhook/deliver.go

+33-3
Original file line numberDiff line numberDiff line change
@@ -12,11 +12,13 @@ import (
1212
"net/http"
1313
"net/url"
1414
"strings"
15+
"sync"
1516
"time"
1617

1718
"code.gitea.io/gitea/models"
1819
"code.gitea.io/gitea/modules/log"
1920
"code.gitea.io/gitea/modules/setting"
21+
"github.com/gobwas/glob"
2022
"github.com/unknwon/com"
2123
)
2224

@@ -182,7 +184,36 @@ func DeliverHooks() {
182184
}
183185
}
184186

185-
var webhookHTTPClient *http.Client
187+
var (
188+
webhookHTTPClient *http.Client
189+
once sync.Once
190+
hostMatchers []glob.Glob
191+
)
192+
193+
func webhookProxy() func(req *http.Request) (*url.URL, error) {
194+
if setting.Webhook.ProxyURL == "" {
195+
return http.ProxyFromEnvironment
196+
}
197+
198+
once.Do(func() {
199+
for _, h := range setting.Webhook.ProxyHosts {
200+
if g, err := glob.Compile(h); err == nil {
201+
hostMatchers = append(hostMatchers, g)
202+
} else {
203+
log.Error("glob.Compile %s failed: %v", h, err)
204+
}
205+
}
206+
})
207+
208+
return func(req *http.Request) (*url.URL, error) {
209+
for _, v := range hostMatchers {
210+
if v.Match(req.URL.Host) {
211+
return http.ProxyURL(setting.Webhook.ProxyURLFixed)(req)
212+
}
213+
}
214+
return http.ProxyFromEnvironment(req)
215+
}
216+
}
186217

187218
// InitDeliverHooks starts the hooks delivery thread
188219
func InitDeliverHooks() {
@@ -191,15 +222,14 @@ func InitDeliverHooks() {
191222
webhookHTTPClient = &http.Client{
192223
Transport: &http.Transport{
193224
TLSClientConfig: &tls.Config{InsecureSkipVerify: setting.Webhook.SkipTLSVerify},
194-
Proxy: http.ProxyFromEnvironment,
225+
Proxy: webhookProxy(),
195226
Dial: func(netw, addr string) (net.Conn, error) {
196227
conn, err := net.DialTimeout(netw, addr, timeout)
197228
if err != nil {
198229
return nil, err
199230
}
200231

201232
return conn, conn.SetDeadline(time.Now().Add(timeout))
202-
203233
},
204234
},
205235
}

modules/webhook/deliver_test.go

+39
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
// Copyright 2019 The Gitea Authors. All rights reserved.
2+
// Use of this source code is governed by a MIT-style
3+
// license that can be found in the LICENSE file.
4+
5+
package webhook
6+
7+
import (
8+
"net/http"
9+
"net/url"
10+
"testing"
11+
12+
"code.gitea.io/gitea/modules/setting"
13+
"github.com/stretchr/testify/assert"
14+
)
15+
16+
func TestWebhookProxy(t *testing.T) {
17+
setting.Webhook.ProxyURL = "http://localhost:8080"
18+
setting.Webhook.ProxyURLFixed, _ = url.Parse(setting.Webhook.ProxyURL)
19+
setting.Webhook.ProxyHosts = []string{"*.discordapp.com", "discordapp.com"}
20+
21+
var kases = map[string]string{
22+
"https://discordapp.com/api/webhooks/xxxxxxxxx/xxxxxxxxxxxxxxxxxxx": "http://localhost:8080",
23+
"http://s.discordapp.com/assets/xxxxxx": "http://localhost:8080",
24+
"http://github.com/a/b": "",
25+
}
26+
27+
for reqURL, proxyURL := range kases {
28+
req, err := http.NewRequest("POST", reqURL, nil)
29+
assert.NoError(t, err)
30+
31+
u, err := webhookProxy()(req)
32+
assert.NoError(t, err)
33+
if proxyURL == "" {
34+
assert.Nil(t, u)
35+
} else {
36+
assert.EqualValues(t, proxyURL, u.String())
37+
}
38+
}
39+
}

0 commit comments

Comments
 (0)