diff options
author | wxiaoguang <wxiaoguang@gmail.com> | 2021-11-01 16:39:52 +0800 |
---|---|---|
committer | GitHub <noreply@github.com> | 2021-11-01 16:39:52 +0800 |
commit | 599ff1c054e436daa4dc3f049aa8661d9c2395f9 (patch) | |
tree | 800983fd2e9d9de3dd1977738d18b64df34dd9ea /services | |
parent | 4e8a81780ed4ff0423e3a2ac7f75265e362ca46d (diff) | |
download | gitea-599ff1c054e436daa4dc3f049aa8661d9c2395f9.tar.gz gitea-599ff1c054e436daa4dc3f049aa8661d9c2395f9.zip |
Only allow webhook to send requests to allowed hosts (#17482)
Diffstat (limited to 'services')
-rw-r--r-- | services/webhook/deliver.go | 26 |
1 files changed, 22 insertions, 4 deletions
diff --git a/services/webhook/deliver.go b/services/webhook/deliver.go index 28c3b23b2f..04cec4c1c4 100644 --- a/services/webhook/deliver.go +++ b/services/webhook/deliver.go @@ -19,6 +19,7 @@ import ( "strconv" "strings" "sync" + "syscall" "time" "code.gitea.io/gitea/models" @@ -29,6 +30,8 @@ import ( "github.com/gobwas/glob" ) +var contextKeyWebhookRequest interface{} = "contextKeyWebhookRequest" + // Deliver deliver hook task func Deliver(t *models.HookTask) error { w, err := models.GetWebhookByID(t.HookID) @@ -171,7 +174,7 @@ func Deliver(t *models.HookTask) error { return fmt.Errorf("Webhook task skipped (webhooks disabled): [%d]", t.ID) } - resp, err := webhookHTTPClient.Do(req) + resp, err := webhookHTTPClient.Do(req.WithContext(context.WithValue(req.Context(), contextKeyWebhookRequest, req))) if err != nil { t.ResponseInfo.Body = fmt.Sprintf("Delivery: %v", err) return err @@ -293,14 +296,29 @@ func InitDeliverHooks() { timeout := time.Duration(setting.Webhook.DeliverTimeout) * time.Second webhookHTTPClient = &http.Client{ + Timeout: timeout, Transport: &http.Transport{ TLSClientConfig: &tls.Config{InsecureSkipVerify: setting.Webhook.SkipTLSVerify}, Proxy: webhookProxy(), - Dial: func(netw, addr string) (net.Conn, error) { - return net.DialTimeout(netw, addr, timeout) // dial timeout + DialContext: func(ctx context.Context, network, addrOrHost string) (net.Conn, error) { + dialer := net.Dialer{ + Timeout: timeout, + Control: func(network, ipAddr string, c syscall.RawConn) error { + // in Control func, the addr was already resolved to IP:PORT format, there is no cost to do ResolveTCPAddr here + tcpAddr, err := net.ResolveTCPAddr(network, ipAddr) + req := ctx.Value(contextKeyWebhookRequest).(*http.Request) + if err != nil { + return fmt.Errorf("webhook can only call HTTP servers via TCP, deny '%s(%s:%s)', err=%v", req.Host, network, ipAddr, err) + } + if !setting.Webhook.AllowedHostList.MatchesHostOrIP(req.Host, tcpAddr.IP) { + return fmt.Errorf("webhook can only call allowed HTTP servers (check your webhook.ALLOWED_HOST_LIST setting), deny '%s(%s)'", req.Host, ipAddr) + } + return nil + }, + } + return dialer.DialContext(ctx, network, addrOrHost) }, }, - Timeout: timeout, // request timeout } go graceful.GetManager().RunWithShutdownContext(DeliverHooks) |