Signed-off-by: dawn dawn@tangled.org
+74
-28
Diff
Round #8
+6
-28
appview/serververify/verify.go
+6
-28
appview/serververify/verify.go
···
4
4
"context"
5
5
"errors"
6
6
"fmt"
7
-
"net"
8
7
"net/http"
9
-
"syscall"
10
8
"time"
11
9
12
10
indigoxrpc "github.com/bluesky-social/indigo/xrpc"
13
11
"tangled.org/core/api/tangled"
14
12
"tangled.org/core/appview/db"
13
+
"tangled.org/core/netutil"
15
14
"tangled.org/core/orm"
16
15
"tangled.org/core/rbac"
17
16
"tangled.org/core/xrpc/xrpcclient"
···
31
30
}
32
31
33
32
host := fmt.Sprintf("%s://%s", scheme, domain)
33
+
dialer := netutil.SSRFDialer(dev)
34
+
dialer.Timeout = 5 * time.Second
35
+
dialer.KeepAlive = 30 * time.Second
36
+
34
37
transport := &http.Transport{
35
-
DialContext: safeDialer(dev).DialContext,
38
+
DialContext: dialer.DialContext,
36
39
}
37
40
xrpcc := &indigoxrpc.Client{
38
41
Host: host,
···
176
179
177
180
return nil
178
181
}
179
-
func safeDialer(dev bool) *net.Dialer {
180
-
d := &net.Dialer{
181
-
Timeout: 5 * time.Second,
182
-
KeepAlive: 30 * time.Second,
183
-
}
184
-
if dev {
185
-
return d
186
-
}
187
-
d.Control = func(network, address string, _ syscall.RawConn) error {
188
-
host, _, err := net.SplitHostPort(address)
189
-
if err != nil {
190
-
return fmt.Errorf("invalid dial address %q: %w", address, err)
191
-
}
192
-
ip := net.ParseIP(host)
193
-
if ip == nil {
194
-
return fmt.Errorf("dial address %q did not resolve to IP", address)
195
-
}
196
-
if ip.IsLoopback() || ip.IsPrivate() || ip.IsLinkLocalUnicast() ||
197
-
ip.IsLinkLocalMulticast() || ip.IsMulticast() || ip.IsUnspecified() {
198
-
return fmt.Errorf("refusing to dial %s: reserved or private address", ip)
199
-
}
200
-
return nil
201
-
}
202
-
return d
203
-
}
+51
netutil/ssrf.go
+51
netutil/ssrf.go
···
1
+
package netutil
2
+
3
+
import (
4
+
"fmt"
5
+
"net"
6
+
"net/http"
7
+
"net/url"
8
+
9
+
"github.com/bluesky-social/indigo/util/ssrf"
10
+
"github.com/gorilla/websocket"
11
+
)
12
+
13
+
// SSRFDialer returns a net.Dialer that refuses non-public IPs.
14
+
func SSRFDialer(dev bool) *net.Dialer {
15
+
if dev {
16
+
return &net.Dialer{}
17
+
}
18
+
return ssrf.PublicOnlyDialer()
19
+
}
20
+
21
+
// SSRFTransport returns an http.Transport that refuses non-public IPs.
22
+
func SSRFTransport(dev bool) *http.Transport {
23
+
if dev {
24
+
return &http.Transport{}
25
+
}
26
+
return ssrf.PublicOnlyTransport()
27
+
}
28
+
29
+
// SSRFWebsocketDialer returns a websocket.Dialer that refuses non-public IPs.
30
+
func SSRFWebsocketDialer(dev bool) *websocket.Dialer {
31
+
dialer := *websocket.DefaultDialer
32
+
dialer.NetDialContext = SSRFDialer(dev).DialContext
33
+
return &dialer
34
+
}
35
+
36
+
func EnforceWSSURL(rawURL string, dev bool) (*url.URL, error) {
37
+
u, err := url.Parse(rawURL)
38
+
if err != nil {
39
+
return nil, fmt.Errorf("invalid url: %w", err)
40
+
}
41
+
switch u.Scheme {
42
+
case "wss":
43
+
case "ws":
44
+
if !dev {
45
+
return nil, fmt.Errorf("insecure scheme %q is prohibited in production; use wss://", u.Scheme)
46
+
}
47
+
default:
48
+
return nil, fmt.Errorf("unsupported websocket scheme %q", u.Scheme)
49
+
}
50
+
return u, nil
51
+
}
+17
netutil/ssrf_test.go
+17
netutil/ssrf_test.go
···
1
+
package netutil
2
+
3
+
import (
4
+
"testing"
5
+
6
+
"github.com/gorilla/websocket"
7
+
)
8
+
9
+
func TestSSRFWebsocketDialerPreservesHandshakeTimeout(t *testing.T) {
10
+
dialer := SSRFWebsocketDialer(false)
11
+
if dialer.HandshakeTimeout != websocket.DefaultDialer.HandshakeTimeout {
12
+
t.Fatalf("HandshakeTimeout = %v, want %v", dialer.HandshakeTimeout, websocket.DefaultDialer.HandshakeTimeout)
13
+
}
14
+
if dialer.NetDialContext == nil {
15
+
t.Fatal("NetDialContext is nil; public-only dialing is not enforced")
16
+
}
17
+
}
History
9 rounds
0 comments
1 commit
Expand
Collapse
netutil: extract common SSRF and secure-scheme guards
Signed-off-by: dawn <dawn@tangled.org>
3/3 success
Expand
Collapse
Expand 0 comments
1 commit
Expand
Collapse
netutil: extract common SSRF and secure-scheme guards
Signed-off-by: dawn <dawn@tangled.org>
3/3 success
Expand
Collapse
Expand 0 comments
1 commit
Expand
Collapse
netutil: extract common SSRF and secure-scheme guards
Signed-off-by: dawn <dawn@tangled.org>
2/3 success, 1/3 failed
Expand
Collapse
Expand 0 comments
1 commit
Expand
Collapse
netutil: extract common SSRF and secure-scheme guards
Signed-off-by: dawn <dawn@tangled.org>
3/3 success
Expand
Collapse
Expand 0 comments
1 commit
Expand
Collapse
netutil: extract common SSRF and secure-scheme guards
Signed-off-by: dawn <dawn@tangled.org>
3/3 success
Expand
Collapse
Expand 0 comments
1 commit
Expand
Collapse
netutil: extract common SSRF and secure-scheme guards
Signed-off-by: dawn <dawn@tangled.org>
3/3 success
Expand
Collapse
Expand 0 comments
1 commit
Expand
Collapse
netutil: extract common SSRF and secure-scheme guards
Signed-off-by: dawn <dawn@tangled.org>
2/3 success, 1/3 failed
Expand
Collapse
Expand 0 comments
1 commit
Expand
Collapse
netutil: extract common SSRF and secure-scheme guards
Signed-off-by: dawn <dawn@tangled.org>
2/3 success, 1/3 failed
Expand
Collapse
Expand 0 comments
1 commit
Expand
Collapse
netutil: extract common SSRF and secure-scheme guards
Signed-off-by: dawn <dawn@tangled.org>