feat(*): acl · webhookx-io/webhookx@d03d7db
@@ -3,33 +3,80 @@ package deliverer
33import (
44"bytes"
55"context"
6+"fmt"
67"github.com/webhookx-io/webhookx/config"
78"github.com/webhookx-io/webhookx/constants"
89"io"
10+"net"
911"net/http"
12+"net/url"
1013"time"
1114)
121516+type key struct{}
17+1318// HTTPDeliverer delivers via HTTP
1419type HTTPDeliverer struct {
1520 defaultTimeout time.Duration
1621 client *http.Client
1722}
182319-func NewHTTPDeliverer(cfg *config.WorkerDeliverer) *HTTPDeliverer {
24+func restrictedDialFunc(acl *ACL) func(context.Context, string, string) (net.Conn, error) {
25+ dialer := &net.Dialer{}
26+ return func(ctx context.Context, network, addr string) (net.Conn, error) {
27+ host, port, err := net.SplitHostPort(addr)
28+ if err != nil {
29+ return nil, err
30+ }
31+32+ ips, err := net.DefaultResolver.LookupNetIP(ctx, "ip", host)
33+ if err != nil {
34+ return nil, err
35+ }
36+37+ for _, ip := range ips {
38+ if acl.Allow(host, ip) {
39+ return dialer.DialContext(ctx, network, net.JoinHostPort(ip.String(), port))
40+ }
41+ }
42+43+ if res, ok := ctx.Value(key{}).(*Response); ok {
44+ res.ACL.Denied = true
45+ }
46+47+ return nil, fmt.Errorf("request to %s (IP=%s) denied by ACL", host, ips[0])
48+ }
49+}
50+51+func NewHTTPDeliverer(cfg *config.WorkerDeliverer) (*HTTPDeliverer, error) {
52+ transport := &http.Transport{
53+ MaxIdleConns: 1000,
54+ MaxIdleConnsPerHost: 1000,
55+ IdleConnTimeout: 30 * time.Second,
56+ TLSHandshakeTimeout: 5 * time.Second,
57+ ExpectContinueTimeout: 1 * time.Second,
58+ DialContext: restrictedDialFunc(NewACL(AclOptions{Rules: cfg.ACL.Deny})),
59+ }
60+ if cfg.HTTPProxy != "" {
61+ u, err := url.Parse(cfg.HTTPProxy)
62+ if err != nil {
63+ return nil, fmt.Errorf("invalid http proxy url '%s': %s", cfg.HTTPProxy, err)
64+ }
65+ if u.Scheme == "" || u.Host == "" {
66+ return nil, fmt.Errorf("invalid http proxy url: '%s'", cfg.HTTPProxy)
67+ }
68+ transport.Proxy = http.ProxyURL(u)
69+ transport.DialContext = nil
70+ }
71+2072 client := &http.Client{
21- Transport: &http.Transport{
22- MaxIdleConns: 1000,
23- MaxIdleConnsPerHost: 1000,
24- IdleConnTimeout: 30 * time.Second,
25- TLSHandshakeTimeout: 5 * time.Second,
26- ExpectContinueTimeout: 1 * time.Second,
27- },
73+ Transport: transport,
2874 }
75+2976 return &HTTPDeliverer{
3077 defaultTimeout: time.Duration(cfg.Timeout) * time.Millisecond,
3178 client: client,
32- }
79+ }, nil
3380}
34813582func timing(fn func()) time.Duration {
@@ -52,6 +99,7 @@ func (d *HTTPDeliverer) Deliver(ctx context.Context, req *Request) (res *Respons
5299 Request: req,
53100 }
54101102+ ctx = context.WithValue(ctx, key{}, res)
55103 request, err := http.NewRequestWithContext(ctx, req.Method, req.URL, bytes.NewBuffer(req.Payload))
56104 if err != nil {
57105 res.Error = err