mirror of
https://github.com/coder/coder.git
synced 2026-09-22 05:05:20 +08:00
feat: Warn on coderd startup if access URL is localhost (#2248)
This commit is contained in:
@@ -203,6 +203,28 @@ func server() *cobra.Command {
|
||||
_, _ = fmt.Fprintln(cmd.ErrOrStderr())
|
||||
}
|
||||
|
||||
// Warn the user if the access URL appears to be a loopback address.
|
||||
isLocal, err := isLocalURL(cmd.Context(), accessURL)
|
||||
if isLocal || err != nil {
|
||||
var reason string
|
||||
if isLocal {
|
||||
reason = "appears to be a loopback address"
|
||||
} else {
|
||||
reason = "could not be resolved"
|
||||
}
|
||||
_, _ = fmt.Fprintf(cmd.ErrOrStderr(), cliui.Styles.Wrap.Render(
|
||||
cliui.Styles.Warn.Render("Warning:")+" The current access URL:")+"\n\n")
|
||||
_, _ = fmt.Fprintf(cmd.ErrOrStderr(), " "+cliui.Styles.Field.Render(accessURL)+"\n\n")
|
||||
_, _ = fmt.Fprintf(cmd.ErrOrStderr(), cliui.Styles.Wrap.Render(
|
||||
reason+". Provisioned workspaces are unlikely to be able to "+
|
||||
"connect to Coder. Please consider changing your "+
|
||||
"access URL using the --access-url option, or directly "+
|
||||
"specifying access URLs on templates.",
|
||||
)+"\n\n")
|
||||
_, _ = fmt.Fprintf(cmd.ErrOrStderr(), "For more information, see "+
|
||||
"https://github.com/coder/coder/issues/1528\n\n")
|
||||
}
|
||||
|
||||
validator, err := idtoken.NewValidator(cmd.Context(), option.WithoutAuthentication())
|
||||
if err != nil {
|
||||
return err
|
||||
@@ -803,3 +825,24 @@ func serveHandler(ctx context.Context, logger slog.Logger, handler http.Handler,
|
||||
|
||||
return func() { _ = srv.Close() }
|
||||
}
|
||||
|
||||
// isLocalURL returns true if the hostname of the provided URL appears to
|
||||
// resolve to a loopback address.
|
||||
func isLocalURL(ctx context.Context, urlString string) (bool, error) {
|
||||
parsedURL, err := url.Parse(urlString)
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
resolver := &net.Resolver{}
|
||||
ips, err := resolver.LookupIPAddr(ctx, parsedURL.Hostname())
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
|
||||
for _, ip := range ips {
|
||||
if ip.IP.IsLoopback() {
|
||||
return true, nil
|
||||
}
|
||||
}
|
||||
return false, nil
|
||||
}
|
||||
|
||||
@@ -118,6 +118,9 @@ func TestServer(t *testing.T) {
|
||||
} else {
|
||||
t.Error("expected password line output; got no match")
|
||||
}
|
||||
|
||||
// Verify that we warned the user about the default access URL possibly not being what they want.
|
||||
assert.Contains(t, buf.String(), "coder/coder/issues/1528")
|
||||
})
|
||||
|
||||
// Duplicated test from "Development" above to test setting email/password via env.
|
||||
@@ -163,6 +166,32 @@ func TestServer(t *testing.T) {
|
||||
assert.Contains(t, buf.String(), fmt.Sprintf("password: %s", wantPassword), "expected output %q; got no match", wantPassword)
|
||||
})
|
||||
|
||||
t.Run("NoWarningWithRemoteAccessURL", func(t *testing.T) {
|
||||
t.Parallel()
|
||||
ctx, cancelFunc := context.WithCancel(context.Background())
|
||||
defer cancelFunc()
|
||||
|
||||
root, cfg := clitest.New(t, "server", "--dev", "--tunnel=false", "--address", ":0", "--access-url", "http://1.2.3.4:3000/")
|
||||
var buf strings.Builder
|
||||
errC := make(chan error)
|
||||
root.SetOutput(&buf)
|
||||
go func() {
|
||||
errC <- root.ExecuteContext(ctx)
|
||||
}()
|
||||
|
||||
// Just wait for startup
|
||||
require.Eventually(t, func() bool {
|
||||
var err error
|
||||
_, err = cfg.URL().Read()
|
||||
return err == nil
|
||||
}, 15*time.Second, 25*time.Millisecond)
|
||||
|
||||
cancelFunc()
|
||||
require.ErrorIs(t, <-errC, context.Canceled)
|
||||
|
||||
assert.NotContains(t, buf.String(), "coder/coder/issues/1528")
|
||||
})
|
||||
|
||||
t.Run("TLSBadVersion", func(t *testing.T) {
|
||||
t.Parallel()
|
||||
ctx, cancelFunc := context.WithCancel(context.Background())
|
||||
|
||||
Reference in New Issue
Block a user