Movatterモバイル変換


[0]ホーム

URL:


Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

Commit461723e

Browse files
committed
feat(cli): add aws check to ping p2p diagnostics
1 parent6dbfe6f commit461723e

File tree

5 files changed

+292
-11
lines changed

5 files changed

+292
-11
lines changed

‎cli/cliui/agent.go

Lines changed: 29 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -357,10 +357,18 @@ type ConnDiags struct {
357357
LocalNetInfo*tailcfg.NetInfo
358358
LocalInterfaces*healthsdk.InterfacesReport
359359
AgentNetcheck*healthsdk.AgentNetcheckReport
360+
ClientIPIsAWSbool
361+
AgentIPIsAWSbool
360362
// TODO: More diagnostics
361363
}
362364

363365
funcConnDiagnostics(w io.Writer,dConnDiags) {
366+
ifd.PingP2P {
367+
_,_=fmt.Fprint(w,"✔ You are connected directly (p2p)\n")
368+
}else {
369+
_,_=fmt.Fprint(w,"❗ You are connected via a DERP relay, not directly (p2p)\n")
370+
}
371+
364372
ifd.AgentNetcheck!=nil {
365373
for_,msg:=ranged.AgentNetcheck.Interfaces.Warnings {
366374
_,_=fmt.Fprintf(w,"❗ Agent: %s\n",msg.Message)
@@ -373,12 +381,6 @@ func ConnDiagnostics(w io.Writer, d ConnDiags) {
373381
}
374382
}
375383

376-
ifd.PingP2P {
377-
_,_=fmt.Fprint(w,"✔ You are connected directly (p2p)\n")
378-
return
379-
}
380-
_,_=fmt.Fprint(w,"❗ You are connected via a DERP relay, not directly (p2p)\n")
381-
382384
ifd.DisableDirect {
383385
_,_=fmt.Fprint(w,"❗ Direct connections are disabled locally, by `--disable-direct` or `CODER_DISABLE_DIRECT`\n")
384386
return
@@ -389,15 +391,32 @@ func ConnDiagnostics(w io.Writer, d ConnDiags) {
389391
return
390392
}
391393

392-
ifd.ConnInfo!=nil&&d.ConnInfo.DERPMap!=nil&&!d.ConnInfo.DERPMap.HasSTUN() {
393-
_,_=fmt.Fprint(w,"✘ The DERP map is not configured to use STUN, which will prevent direct connections from starting outside of local networks\n")
394+
ifd.ConnInfo!=nil&&d.ConnInfo.DERPMap!=nil {
395+
if!d.ConnInfo.DERPMap.HasSTUN() {
396+
_,_=fmt.Fprint(w,"✘ The DERP map is not configured to use STUN, which will prevent direct connections from starting outside of local networks\n")
397+
}elseifd.LocalNetInfo!=nil&&!d.LocalNetInfo.UDP {
398+
_,_=fmt.Fprint(w,"❗ Client could not connect to STUN over UDP, which may be preventing a direct connection.\n")
399+
}
394400
}
395401

396402
ifd.LocalNetInfo!=nil&&d.LocalNetInfo.MappingVariesByDestIP.EqualBool(true) {
397403
_,_=fmt.Fprint(w,"❗ Client is potentially behind a hard NAT, as multiple endpoints were retrieved from different STUN servers\n")
398404
}
399405

400-
ifd.AgentNetcheck!=nil&&d.AgentNetcheck.NetInfo!=nil&&d.AgentNetcheck.NetInfo.MappingVariesByDestIP.EqualBool(true) {
401-
_,_=fmt.Fprint(w,"❗ Agent is potentially behind a hard NAT, as multiple endpoints were retrieved from different STUN servers\n")
406+
ifd.AgentNetcheck!=nil&&d.AgentNetcheck.NetInfo!=nil {
407+
ifd.AgentNetcheck.NetInfo.MappingVariesByDestIP.EqualBool(true) {
408+
_,_=fmt.Fprint(w,"❗ Agent is potentially behind a hard NAT, as multiple endpoints were retrieved from different STUN servers\n")
409+
}
410+
if!d.AgentNetcheck.NetInfo.UDP {
411+
_,_=fmt.Fprint(w,"❗ Agent could not connect to STUN over UDP, which may be preventing a direct connection.\n")
412+
}
413+
}
414+
415+
ifd.ClientIPIsAWS {
416+
_,_=fmt.Fprint(w,"❗ Client IP address is within an AWS range, which is known to cause problems with forming direct connections (AWS uses hard NAT)\n")
417+
}
418+
419+
ifd.AgentIPIsAWS {
420+
_,_=fmt.Fprint(w,"❗ Agent IP address is within an AWS range, which is known to cause problems with forming direct connections (AWS uses hard NAT)\n")
402421
}
403422
}

‎cli/cliui/agent_test.go

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -782,6 +782,28 @@ func TestConnDiagnostics(t *testing.T) {
782782
`✔ You are connected directly (p2p)`,
783783
},
784784
},
785+
{
786+
name:"ClientAWSIP",
787+
diags: cliui.ConnDiags{
788+
ClientIPIsAWS:true,
789+
AgentIPIsAWS:false,
790+
},
791+
want: []string{
792+
`❗ You are connected via a DERP relay, not directly (p2p)`,
793+
`❗ Client IP address is within an AWS range, which is known to cause problems with forming direct connections (AWS uses hard NAT)`,
794+
},
795+
},
796+
{
797+
name:"AgentAWSIP",
798+
diags: cliui.ConnDiags{
799+
ClientIPIsAWS:false,
800+
AgentIPIsAWS:true,
801+
},
802+
want: []string{
803+
`❗ You are connected via a DERP relay, not directly (p2p)`,
804+
`❗ Agent IP address is within an AWS range, which is known to cause problems with forming direct connections (AWS uses hard NAT)`,
805+
},
806+
},
785807
}
786808
for_,tc:=rangetestCases {
787809
tc:=tc

‎cli/cliutil/awscheck.go

Lines changed: 114 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,114 @@
1+
package cliutil
2+
3+
import (
4+
"context"
5+
"encoding/json"
6+
"io"
7+
"net/http"
8+
"net/netip"
9+
"time"
10+
11+
"golang.org/x/xerrors"
12+
)
13+
14+
constAWSIPRangesURL="https://ip-ranges.amazonaws.com/ip-ranges.json"
15+
16+
typeawsIPv4Prefixstruct {
17+
Prefixstring`json:"ip_prefix"`
18+
Regionstring`json:"region"`
19+
Servicestring`json:"service"`
20+
NetworkBorderGroupstring`json:"network_border_group"`
21+
}
22+
23+
typeawsIPv6Prefixstruct {
24+
Prefixstring`json:"ipv6_prefix"`
25+
Regionstring`json:"region"`
26+
Servicestring`json:"service"`
27+
NetworkBorderGroupstring`json:"network_border_group"`
28+
}
29+
30+
typeAWSIPRangesstruct {
31+
V4 []netip.Prefix
32+
V6 []netip.Prefix
33+
}
34+
35+
typeawsIPRangesResponsestruct {
36+
SyncTokenstring`json:"syncToken"`
37+
CreateDatestring`json:"createDate"`
38+
IPV4Prefixes []awsIPv4Prefix`json:"prefixes"`
39+
IPV6Prefixes []awsIPv6Prefix`json:"ipv6_prefixes"`
40+
}
41+
42+
funcFetchAWSIPRanges(ctx context.Context,urlstring) (*AWSIPRanges,error) {
43+
client:=&http.Client{}
44+
reqCtx,reqCancel:=context.WithTimeout(ctx,5*time.Second)
45+
deferreqCancel()
46+
req,_:=http.NewRequestWithContext(reqCtx,http.MethodGet,url,nil)
47+
resp,err:=client.Do(req)
48+
iferr!=nil {
49+
returnnil,err
50+
}
51+
deferresp.Body.Close()
52+
53+
ifresp.StatusCode!=http.StatusOK {
54+
b,_:=io.ReadAll(resp.Body)
55+
returnnil,xerrors.Errorf("unexpected status code %d: %s",resp.StatusCode,b)
56+
}
57+
58+
varbodyawsIPRangesResponse
59+
err=json.NewDecoder(resp.Body).Decode(&body)
60+
iferr!=nil {
61+
returnnil,xerrors.Errorf("json decode: %w",err)
62+
}
63+
64+
out:=&AWSIPRanges{
65+
V4:make([]netip.Prefix,0,len(body.IPV4Prefixes)),
66+
V6:make([]netip.Prefix,0,len(body.IPV6Prefixes)),
67+
}
68+
69+
for_,p:=rangebody.IPV4Prefixes {
70+
prefix,err:=netip.ParsePrefix(p.Prefix)
71+
iferr!=nil {
72+
returnnil,xerrors.Errorf("parse ip prefix: %w",err)
73+
}
74+
ifprefix.Addr().Is6() {
75+
returnnil,xerrors.Errorf("ipv4 prefix contains ipv6 address: %s",p.Prefix)
76+
}
77+
out.V4=append(out.V4,prefix)
78+
}
79+
80+
for_,p:=rangebody.IPV6Prefixes {
81+
prefix,err:=netip.ParsePrefix(p.Prefix)
82+
iferr!=nil {
83+
returnnil,xerrors.Errorf("parse ip prefix: %w",err)
84+
}
85+
ifprefix.Addr().Is4() {
86+
returnnil,xerrors.Errorf("ipv6 prefix contains ipv4 address: %s",p.Prefix)
87+
}
88+
out.V6=append(out.V6,prefix)
89+
}
90+
91+
returnout,nil
92+
}
93+
94+
// CheckIP checks if the given IP address is an AWS IP.
95+
func (r*AWSIPRanges)CheckIP(ip netip.Addr)bool {
96+
ifip.IsLoopback()||ip.IsLinkLocalMulticast()||ip.IsLinkLocalUnicast()||ip.IsPrivate() {
97+
returnfalse
98+
}
99+
100+
ifip.Is4() {
101+
for_,p:=ranger.V4 {
102+
ifp.Contains(ip) {
103+
returntrue
104+
}
105+
}
106+
}else {
107+
for_,p:=ranger.V6 {
108+
ifp.Contains(ip) {
109+
returntrue
110+
}
111+
}
112+
}
113+
returnfalse
114+
}

‎cli/cliutil/awscheck_internal_test.go

Lines changed: 95 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,95 @@
1+
package cliutil
2+
3+
import (
4+
"context"
5+
"net/http"
6+
"net/http/httptest"
7+
"net/netip"
8+
"testing"
9+
10+
"github.com/stretchr/testify/require"
11+
12+
"github.com/coder/coder/v2/coderd/httpapi"
13+
"github.com/coder/coder/v2/testutil"
14+
)
15+
16+
funcTestIPV4Check(t*testing.T) {
17+
t.Parallel()
18+
srv:=httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter,r*http.Request) {
19+
httpapi.Write(context.Background(),w,http.StatusOK,awsIPRangesResponse{
20+
IPV4Prefixes: []awsIPv4Prefix{
21+
{
22+
Prefix:"3.24.0.0/14",
23+
},
24+
{
25+
Prefix:"15.230.15.29/32",
26+
},
27+
{
28+
Prefix:"47.128.82.100/31",
29+
},
30+
},
31+
IPV6Prefixes: []awsIPv6Prefix{
32+
{
33+
Prefix:"2600:9000:5206::/48",
34+
},
35+
{
36+
Prefix:"2406:da70:8800::/40",
37+
},
38+
{
39+
Prefix:"2600:1f68:5000::/40",
40+
},
41+
},
42+
})
43+
}))
44+
ctx:=testutil.Context(t,testutil.WaitShort)
45+
ranges,err:=FetchAWSIPRanges(ctx,srv.URL)
46+
require.NoError(t,err)
47+
48+
t.Run("Private/IPV4",func(t*testing.T) {
49+
t.Parallel()
50+
ip,err:=netip.ParseAddr("192.168.0.1")
51+
require.NoError(t,err)
52+
isAws:=ranges.CheckIP(ip)
53+
require.False(t,isAws)
54+
})
55+
56+
t.Run("AWS/IPV4",func(t*testing.T) {
57+
t.Parallel()
58+
ip,err:=netip.ParseAddr("3.25.61.113")
59+
require.NoError(t,err)
60+
isAws:=ranges.CheckIP(ip)
61+
require.True(t,isAws)
62+
})
63+
64+
t.Run("NonAWS/IPV4",func(t*testing.T) {
65+
t.Parallel()
66+
ip,err:=netip.ParseAddr("159.196.123.40")
67+
require.NoError(t,err)
68+
isAws:=ranges.CheckIP(ip)
69+
require.False(t,isAws)
70+
})
71+
72+
t.Run("Private/IPV6",func(t*testing.T) {
73+
t.Parallel()
74+
ip,err:=netip.ParseAddr("::1")
75+
require.NoError(t,err)
76+
isAws:=ranges.CheckIP(ip)
77+
require.False(t,isAws)
78+
})
79+
80+
t.Run("AWS/IPV6",func(t*testing.T) {
81+
t.Parallel()
82+
ip,err:=netip.ParseAddr("2600:9000:5206:0001:0000:0000:0000:0001")
83+
require.NoError(t,err)
84+
isAws:=ranges.CheckIP(ip)
85+
require.True(t,isAws)
86+
})
87+
88+
t.Run("NonAWS/IPV6",func(t*testing.T) {
89+
t.Parallel()
90+
ip,err:=netip.ParseAddr("2403:5807:885f:0:a544:49d4:58f8:aedf")
91+
require.NoError(t,err)
92+
isAws:=ranges.CheckIP(ip)
93+
require.False(t,isAws)
94+
})
95+
}

‎cli/ping.go

Lines changed: 32 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,16 +5,19 @@ import (
55
"errors"
66
"fmt"
77
"net/http"
8+
"net/netip"
89
"time"
910

1011
"golang.org/x/xerrors"
12+
"tailscale.com/tailcfg"
1113

1214
"cdr.dev/slog"
1315
"cdr.dev/slog/sloggers/sloghuman"
1416

1517
"github.com/coder/pretty"
1618

1719
"github.com/coder/coder/v2/cli/cliui"
20+
"github.com/coder/coder/v2/cli/cliutil"
1821
"github.com/coder/coder/v2/codersdk"
1922
"github.com/coder/coder/v2/codersdk/healthsdk"
2023
"github.com/coder/coder/v2/codersdk/workspacesdk"
@@ -150,11 +153,20 @@ func (r *RootCmd) ping() *serpent.Command {
150153
diags:=conn.GetPeerDiagnostics()
151154
cliui.PeerDiagnostics(inv.Stdout,diags)
152155

156+
ni:=conn.GetNetInfo()
153157
connDiags:= cliui.ConnDiags{
154158
PingP2P:didP2p,
155159
DisableDirect:r.disableDirect,
156-
LocalNetInfo:conn.GetNetInfo(),
160+
LocalNetInfo:ni,
157161
}
162+
163+
awsRanges,err:=cliutil.FetchAWSIPRanges(ctx,cliutil.AWSIPRangesURL)
164+
iferr!=nil {
165+
_,_=fmt.Fprintf(inv.Stdout,"Failed to retrieve AWS IP ranges: %v\n",err)
166+
}
167+
168+
connDiags.ClientIPIsAWS=isAWSIP(awsRanges,ni)
169+
158170
connInfo,err:=client.AgentConnectionInfoGeneric(ctx)
159171
iferr==nil {
160172
connDiags.ConnInfo=&connInfo
@@ -167,9 +179,11 @@ func (r *RootCmd) ping() *serpent.Command {
167179
}else {
168180
_,_=fmt.Fprintf(inv.Stdout,"Failed to retrieve local interfaces report: %v\n",err)
169181
}
182+
170183
agentNetcheck,err:=conn.Netcheck(ctx)
171184
iferr==nil {
172185
connDiags.AgentNetcheck=&agentNetcheck
186+
connDiags.AgentIPIsAWS=isAWSIP(awsRanges,agentNetcheck.NetInfo)
173187
}else {
174188
varsdkErr*codersdk.Error
175189
iferrors.As(err,&sdkErr)&&sdkErr.StatusCode()==http.StatusNotFound {
@@ -178,6 +192,7 @@ func (r *RootCmd) ping() *serpent.Command {
178192
_,_=fmt.Fprintf(inv.Stdout,"Failed to retrieve connection report from agent: %v\n",err)
179193
}
180194
}
195+
181196
cliui.ConnDiagnostics(inv.Stdout,connDiags)
182197
returnnil
183198
},
@@ -207,3 +222,19 @@ func (r *RootCmd) ping() *serpent.Command {
207222
}
208223
returncmd
209224
}
225+
226+
funcisAWSIP(awsRanges*cliutil.AWSIPRanges,ni*tailcfg.NetInfo)bool {
227+
ifni.GlobalV4!="" {
228+
ip,err:=netip.ParseAddr(ni.GlobalV4)
229+
iferr==nil&&awsRanges.CheckIP(ip) {
230+
returntrue
231+
}
232+
}
233+
ifni.GlobalV6!="" {
234+
ip,err:=netip.ParseAddr(ni.GlobalV6)
235+
iferr==nil&&awsRanges.CheckIP(ip) {
236+
returntrue
237+
}
238+
}
239+
returnfalse
240+
}

0 commit comments

Comments
 (0)

[8]ページ先頭

©2009-2025 Movatter.jp