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

Lines changed: 43 additions & 0 deletions
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

Lines changed: 29 additions & 0 deletions
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)
pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy