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

Commitef7f40a

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

File tree

5 files changed

+237
-2
lines changed

5 files changed

+237
-2
lines changed

‎cli/cliui/agent.go

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -357,6 +357,8 @@ 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

@@ -375,7 +377,6 @@ func ConnDiagnostics(w io.Writer, d ConnDiags) {
375377

376378
ifd.PingP2P {
377379
_,_=fmt.Fprint(w,"✔ You are connected directly (p2p)\n")
378-
return
379380
}
380381
_,_=fmt.Fprint(w,"❗ You are connected via a DERP relay, not directly (p2p)\n")
381382

@@ -400,4 +401,12 @@ func ConnDiagnostics(w io.Writer, d ConnDiags) {
400401
ifd.AgentNetcheck!=nil&&d.AgentNetcheck.NetInfo!=nil&&d.AgentNetcheck.NetInfo.MappingVariesByDestIP.EqualBool(true) {
401402
_,_=fmt.Fprint(w,"❗ Agent is potentially behind a hard NAT, as multiple endpoints were retrieved from different STUN servers\n")
402403
}
404+
405+
ifd.ClientIPIsAWS {
406+
_,_=fmt.Fprint(w,"❗ Client IP address is within an AWS range, and is therefore behind a hard NAT\n")
407+
}
408+
409+
ifd.AgentIPIsAWS {
410+
_,_=fmt.Fprint(w,"❗ Agent IP address is within an AWS range, and is therefore behind a hard NAT\n")
411+
}
403412
}

‎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, and is therefore behind a 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, and is therefore behind a hard NAT`,
805+
},
806+
},
785807
}
786808
for_,tc:=rangetestCases {
787809
tc:=tc

‎cli/cliutil/awscheck.go

Lines changed: 88 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,88 @@
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+
}
28+
29+
typeAWSIPRangesstruct {
30+
SyncTokenstring`json:"syncToken"`
31+
CreateDatestring`json:"createDate"`
32+
IPV4Prefixes []AWSIPv4Prefix`json:"prefixes"`
33+
IPV6Prefixes []AWSIPv6Prefix`json:"ipv6_prefixes"`
34+
}
35+
36+
funcNewAWSIPRanges(ctx context.Context) (*AWSIPRanges,error) {
37+
client:=&http.Client{}
38+
reqCtx,reqCancel:=context.WithTimeout(ctx,5*time.Second)
39+
deferreqCancel()
40+
req,_:=http.NewRequestWithContext(reqCtx,http.MethodGet,awsIPRangesURL,nil)
41+
resp,err:=client.Do(req)
42+
iferr!=nil {
43+
returnnil,err
44+
}
45+
deferresp.Body.Close()
46+
47+
ifresp.StatusCode!=http.StatusOK {
48+
b,_:=io.ReadAll(resp.Body)
49+
returnnil,xerrors.Errorf("unexpected status code %d: %s",resp.StatusCode,b)
50+
}
51+
52+
varoutAWSIPRanges
53+
err=json.NewDecoder(resp.Body).Decode(&out)
54+
iferr!=nil {
55+
returnnil,xerrors.Errorf("json decode: %w",err)
56+
}
57+
return&out,nil
58+
}
59+
60+
// CheckIP checks if the given IP address is an AWS IP.
61+
func (r*AWSIPRanges)CheckIP(ip netip.Addr) (bool,error) {
62+
ifip.IsLoopback()||ip.IsLinkLocalMulticast()||ip.IsLinkLocalUnicast()||ip.IsPrivate() {
63+
returnfalse,nil
64+
}
65+
66+
ifip.Is4() {
67+
for_,p:=ranger.IPV4Prefixes {
68+
prefix,err:=netip.ParsePrefix(p.Prefix)
69+
iferr!=nil {
70+
returnfalse,xerrors.Errorf("parse ip prefix: %w",err)
71+
}
72+
ifprefix.Contains(ip) {
73+
returntrue,nil
74+
}
75+
}
76+
}else {
77+
for_,p:=ranger.IPV6Prefixes {
78+
prefix,err:=netip.ParsePrefix(p.Prefix)
79+
iferr!=nil {
80+
returnfalse,xerrors.Errorf("parse ip prefix: %w",err)
81+
}
82+
ifprefix.Contains(ip) {
83+
returntrue,nil
84+
}
85+
}
86+
}
87+
returnfalse,nil
88+
}

‎cli/cliutil/awscheck_test.go

Lines changed: 72 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,72 @@
1+
package cliutil_test
2+
3+
import (
4+
"net/netip"
5+
"testing"
6+
7+
"github.com/stretchr/testify/require"
8+
9+
"github.com/coder/coder/v2/cli/cliutil"
10+
"github.com/coder/coder/v2/testutil"
11+
)
12+
13+
funcTestIPV4Check(t*testing.T) {
14+
t.Parallel()
15+
ctx:=testutil.Context(t,testutil.WaitShort)
16+
ranges,err:=cliutil.NewAWSIPRanges(ctx)
17+
require.NoError(t,err)
18+
19+
t.Run("Private/IPV4",func(t*testing.T) {
20+
t.Parallel()
21+
ip,err:=netip.ParseAddr("192.168.0.1")
22+
require.NoError(t,err)
23+
isAws,err:=ranges.CheckIP(ip)
24+
require.NoError(t,err)
25+
require.False(t,isAws)
26+
})
27+
28+
t.Run("AWS/IPV4",func(t*testing.T) {
29+
t.Parallel()
30+
ip,err:=netip.ParseAddr("3.25.61.113")
31+
require.NoError(t,err)
32+
isAws,err:=ranges.CheckIP(ip)
33+
require.NoError(t,err)
34+
require.True(t,isAws)
35+
})
36+
37+
t.Run("NonAWS/IPV4",func(t*testing.T) {
38+
t.Parallel()
39+
ip,err:=netip.ParseAddr("159.196.123.40")
40+
require.NoError(t,err)
41+
isAws,err:=ranges.CheckIP(ip)
42+
require.NoError(t,err)
43+
require.False(t,isAws)
44+
})
45+
46+
t.Run("Private/IPV6",func(t*testing.T) {
47+
t.Parallel()
48+
ip,err:=netip.ParseAddr("::1")
49+
require.NoError(t,err)
50+
isAws,err:=ranges.CheckIP(ip)
51+
require.NoError(t,err)
52+
require.False(t,isAws)
53+
})
54+
55+
t.Run("AWS/IPV6",func(t*testing.T) {
56+
t.Parallel()
57+
ip,err:=netip.ParseAddr("2600:9000:5206:0001:0000:0000:0000:0001")
58+
require.NoError(t,err)
59+
isAws,err:=ranges.CheckIP(ip)
60+
require.NoError(t,err)
61+
require.True(t,isAws)
62+
})
63+
64+
t.Run("NonAWS/IPV6",func(t*testing.T) {
65+
t.Parallel()
66+
ip,err:=netip.ParseAddr("2403:5807:885f:0:a544:49d4:58f8:aedf")
67+
require.NoError(t,err)
68+
isAws,err:=ranges.CheckIP(ip)
69+
require.NoError(t,err)
70+
require.False(t,isAws)
71+
})
72+
}

‎cli/ping.go

Lines changed: 45 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,24 @@ 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.NewAWSIPRanges(ctx)
164+
iferr!=nil {
165+
_,_=fmt.Fprintf(inv.Stdout,"Failed to retrieve AWS IP ranges: %v\n",err)
166+
}
167+
168+
clientIPIsAWS,err:=isAWSIP(awsRanges,ni)
169+
iferr!=nil {
170+
_,_=fmt.Fprintf(inv.Stdout,"Failed to determine if client IP is AWS: %v\n",err)
171+
}
172+
connDiags.ClientIPIsAWS=clientIPIsAWS
173+
158174
connInfo,err:=client.AgentConnectionInfoGeneric(ctx)
159175
iferr==nil {
160176
connDiags.ConnInfo=&connInfo
@@ -167,9 +183,15 @@ func (r *RootCmd) ping() *serpent.Command {
167183
}else {
168184
_,_=fmt.Fprintf(inv.Stdout,"Failed to retrieve local interfaces report: %v\n",err)
169185
}
186+
170187
agentNetcheck,err:=conn.Netcheck(ctx)
171188
iferr==nil {
172189
connDiags.AgentNetcheck=&agentNetcheck
190+
agentIPIsAws,err:=isAWSIP(awsRanges,agentNetcheck.NetInfo)
191+
iferr!=nil {
192+
_,_=fmt.Fprintf(inv.Stdout,"Failed to determine if agent IP is AWS: %v\n",err)
193+
}
194+
connDiags.AgentIPIsAWS=agentIPIsAws
173195
}else {
174196
varsdkErr*codersdk.Error
175197
iferrors.As(err,&sdkErr)&&sdkErr.StatusCode()==http.StatusNotFound {
@@ -178,6 +200,7 @@ func (r *RootCmd) ping() *serpent.Command {
178200
_,_=fmt.Fprintf(inv.Stdout,"Failed to retrieve connection report from agent: %v\n",err)
179201
}
180202
}
203+
181204
cliui.ConnDiagnostics(inv.Stdout,connDiags)
182205
returnnil
183206
},
@@ -207,3 +230,24 @@ func (r *RootCmd) ping() *serpent.Command {
207230
}
208231
returncmd
209232
}
233+
234+
funcisAWSIP(awsRanges*cliutil.AWSIPRanges,ni*tailcfg.NetInfo) (bool,error) {
235+
varstrIPstring
236+
ifni.GlobalV4!="" {
237+
strIP=ni.GlobalV4
238+
}elseifni.GlobalV6!="" {
239+
strIP=ni.GlobalV6
240+
}else {
241+
returnfalse,xerrors.Errorf("no public IP address found")
242+
}
243+
244+
ip,err:=netip.ParseAddr(strIP)
245+
iferr!=nil {
246+
returnfalse,err
247+
}
248+
isAWS,err:=awsRanges.CheckIP(ip)
249+
iferr!=nil {
250+
returnfalse,err
251+
}
252+
returnisAWS,nil
253+
}

0 commit comments

Comments
 (0)

[8]ページ先頭

©2009-2025 Movatter.jp