Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Commit 71fd196

Browse files
authored
feat: Warn on coderd startup if access URL is localhost (#2248)
1 parent f79ab7f commit 71fd196

File tree

2 files changed

+72
-0
lines changed

2 files changed

+72
-0
lines changed

cli/server.go

+43
Original file line numberDiff line numberDiff line change
@@ -203,6 +203,28 @@ func server() *cobra.Command {
203203
_, _ = fmt.Fprintln(cmd.ErrOrStderr())
204204
}
205205

206+
// Warn the user if the access URL appears to be a loopback address.
207+
isLocal, err := isLocalURL(cmd.Context(), accessURL)
208+
if isLocal || err != nil {
209+
var reason string
210+
if isLocal {
211+
reason = "appears to be a loopback address"
212+
} else {
213+
reason = "could not be resolved"
214+
}
215+
_, _ = fmt.Fprintf(cmd.ErrOrStderr(), cliui.Styles.Wrap.Render(
216+
cliui.Styles.Warn.Render("Warning:")+" The current access URL:")+"\n\n")
217+
_, _ = fmt.Fprintf(cmd.ErrOrStderr(), " "+cliui.Styles.Field.Render(accessURL)+"\n\n")
218+
_, _ = fmt.Fprintf(cmd.ErrOrStderr(), cliui.Styles.Wrap.Render(
219+
reason+". Provisioned workspaces are unlikely to be able to "+
220+
"connect to Coder. Please consider changing your "+
221+
"access URL using the --access-url option, or directly "+
222+
"specifying access URLs on templates.",
223+
)+"\n\n")
224+
_, _ = fmt.Fprintf(cmd.ErrOrStderr(), "For more information, see "+
225+
"https://github.com/coder/coder/issues/1528\n\n")
226+
}
227+
206228
validator, err := idtoken.NewValidator(cmd.Context(), option.WithoutAuthentication())
207229
if err != nil {
208230
return err
@@ -803,3 +825,24 @@ func serveHandler(ctx context.Context, logger slog.Logger, handler http.Handler,
803825

804826
return func() { _ = srv.Close() }
805827
}
828+
829+
// isLocalURL returns true if the hostname of the provided URL appears to
830+
// resolve to a loopback address.
831+
func isLocalURL(ctx context.Context, urlString string) (bool, error) {
832+
parsedURL, err := url.Parse(urlString)
833+
if err != nil {
834+
return false, err
835+
}
836+
resolver := &net.Resolver{}
837+
ips, err := resolver.LookupIPAddr(ctx, parsedURL.Hostname())
838+
if err != nil {
839+
return false, err
840+
}
841+
842+
for _, ip := range ips {
843+
if ip.IP.IsLoopback() {
844+
return true, nil
845+
}
846+
}
847+
return false, nil
848+
}

cli/server_test.go

+29
Original file line numberDiff line numberDiff line change
@@ -118,6 +118,9 @@ func TestServer(t *testing.T) {
118118
} else {
119119
t.Error("expected password line output; got no match")
120120
}
121+
122+
// Verify that we warned the user about the default access URL possibly not being what they want.
123+
assert.Contains(t, buf.String(), "coder/coder/issues/1528")
121124
})
122125

123126
// Duplicated test from "Development" above to test setting email/password via env.
@@ -163,6 +166,32 @@ func TestServer(t *testing.T) {
163166
assert.Contains(t, buf.String(), fmt.Sprintf("password: %s", wantPassword), "expected output %q; got no match", wantPassword)
164167
})
165168

169+
t.Run("NoWarningWithRemoteAccessURL", func(t *testing.T) {
170+
t.Parallel()
171+
ctx, cancelFunc := context.WithCancel(context.Background())
172+
defer cancelFunc()
173+
174+
root, cfg := clitest.New(t, "server", "--dev", "--tunnel=false", "--address", ":0", "--access-url", "http://1.2.3.4:3000/")
175+
var buf strings.Builder
176+
errC := make(chan error)
177+
root.SetOutput(&buf)
178+
go func() {
179+
errC <- root.ExecuteContext(ctx)
180+
}()
181+
182+
// Just wait for startup
183+
require.Eventually(t, func() bool {
184+
var err error
185+
_, err = cfg.URL().Read()
186+
return err == nil
187+
}, 15*time.Second, 25*time.Millisecond)
188+
189+
cancelFunc()
190+
require.ErrorIs(t, <-errC, context.Canceled)
191+
192+
assert.NotContains(t, buf.String(), "coder/coder/issues/1528")
193+
})
194+
166195
t.Run("TLSBadVersion", func(t *testing.T) {
167196
t.Parallel()
168197
ctx, cancelFunc := context.WithCancel(context.Background())

0 commit comments

Comments
 (0)