Skip to content

Commit 5882d6e

Browse files
committed
feat: Add tunnel by default
If an access URL is not specified, we will always tunnel. This is from community-member feedback who exclaimed that it's confusing having the default for `coder server` display a warning message, and I agree. There is very little (maybe none) in running `coder server` without tunnel and without an access URL, so this seems like overall a much better UX.
1 parent fe7c9f8 commit 5882d6e

File tree

20 files changed

+59
-87
lines changed

20 files changed

+59
-87
lines changed

README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -58,7 +58,7 @@ Once installed, you can start a production deployment<sup>1</sup> with a single
5858

5959
```sh
6060
# Automatically sets up an external access URL on *.try.coder.app
61-
coder server --tunnel
61+
coder server
6262

6363
# Requires a PostgreSQL instance and external access URL
6464
coder server --postgres-url <url> --access-url <url>

cli/server.go

Lines changed: 21 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -111,7 +111,6 @@ func Server(newAPI func(context.Context, *coderd.Options) (*coderd.API, error))
111111
tlsEnable bool
112112
tlsKeyFiles []string
113113
tlsMinVersion string
114-
tunnel bool
115114
traceEnable bool
116115
secureAuthCookie bool
117116
sshKeygenAlgorithmRaw string
@@ -243,26 +242,23 @@ func Server(newAPI func(context.Context, *coderd.Options) (*coderd.API, error))
243242
if tlsEnable {
244243
localURL.Scheme = "https"
245244
}
246-
if accessURL == "" {
247-
accessURL = localURL.String()
248-
}
249245

250246
var (
251247
ctxTunnel, closeTunnel = context.WithCancel(ctx)
252-
devTunnel *devtunnel.Tunnel
253-
devTunnelErr <-chan error
248+
tunnel *devtunnel.Tunnel
249+
tunnelErr <-chan error
254250
)
255251
defer closeTunnel()
256252

257-
// If we're attempting to tunnel in dev-mode, the access URL
258-
// needs to be changed to use the tunnel.
259-
if tunnel {
260-
cmd.Printf("Opening tunnel so workspaces can connect to your deployment\n")
261-
devTunnel, devTunnelErr, err = devtunnel.New(ctxTunnel, logger.Named("devtunnel"))
253+
// If the access URL is empty, we attempt to run a reverse-proxy tunnel
254+
// to make the initial setup really simple.
255+
if accessURL == "" {
256+
cmd.Printf("Opening tunnel so workspaces can connect to your deployment. For production scenarios, specify an external access URL\n")
257+
tunnel, tunnelErr, err = devtunnel.New(ctxTunnel, logger.Named("devtunnel"))
262258
if err != nil {
263259
return xerrors.Errorf("create tunnel: %w", err)
264260
}
265-
accessURL = devTunnel.URL
261+
accessURL = tunnel.URL
266262
}
267263

268264
accessURLParsed, err := parseURL(ctx, accessURL)
@@ -288,11 +284,11 @@ func Server(newAPI func(context.Context, *coderd.Options) (*coderd.API, error))
288284
if isLocal {
289285
reason = "isn't externally reachable"
290286
}
291-
cmd.Printf("%s The access URL %s %s, this may cause unexpected problems when creating workspaces. Generate a unique *.try.coder.app URL with:\n", cliui.Styles.Warn.Render("Warning:"), cliui.Styles.Field.Render(accessURLParsed.String()), reason)
292-
cmd.Println(cliui.Styles.Code.Render(strings.Join(os.Args, " ") + " --tunnel"))
287+
cmd.Printf("%s The access URL %s %s, this may cause unexpected problems when creating workspaces. Generate a unique *.try.coder.app URL by not specifying an access URL.\n", cliui.Styles.Warn.Render("Warning:"), cliui.Styles.Field.Render(accessURLParsed.String()), reason)
293288
}
294289

295-
cmd.Printf("View the Web UI: %s\n", accessURLParsed.String())
290+
// A newline is added before for visibility in terminal output.
291+
cmd.Printf("\nView the Web UI: %s\n", accessURLParsed.String())
296292

297293
// Used for zero-trust instance identity with Google Cloud.
298294
googleTokenValidator, err := idtoken.NewValidator(ctx, option.WithoutAuthentication())
@@ -472,7 +468,7 @@ func Server(newAPI func(context.Context, *coderd.Options) (*coderd.API, error))
472468
OIDCIssuerURL: oidcIssuerURL,
473469
Prometheus: promEnabled,
474470
STUN: len(derpServerSTUNAddrs) != 0,
475-
Tunnel: tunnel,
471+
Tunnel: tunnel != nil,
476472
})
477473
if err != nil {
478474
return xerrors.Errorf("create telemetry reporter: %w", err)
@@ -569,17 +565,17 @@ func Server(newAPI func(context.Context, *coderd.Options) (*coderd.API, error))
569565
eg.Go(func() error {
570566
// Make sure to close the tunnel listener if we exit so the
571567
// errgroup doesn't wait forever!
572-
if tunnel {
573-
defer devTunnel.Listener.Close()
568+
if tunnel != nil {
569+
defer tunnel.Listener.Close()
574570
}
575571

576572
return server.Serve(listener)
577573
})
578-
if tunnel {
574+
if tunnel != nil {
579575
eg.Go(func() error {
580576
defer listener.Close()
581577

582-
return server.Serve(devTunnel.Listener)
578+
return server.Serve(tunnel.Listener)
583579
})
584580
}
585581
go func() {
@@ -624,7 +620,7 @@ func Server(newAPI func(context.Context, *coderd.Options) (*coderd.API, error))
624620
_, _ = fmt.Fprintln(cmd.OutOrStdout(), cliui.Styles.Bold.Render(
625621
"Interrupt caught, gracefully exiting. Use ctrl+\\ to force quit",
626622
))
627-
case exitErr = <-devTunnelErr:
623+
case exitErr = <-tunnelErr:
628624
if exitErr == nil {
629625
exitErr = xerrors.New("dev tunnel closed unexpectedly")
630626
}
@@ -650,9 +646,9 @@ func Server(newAPI func(context.Context, *coderd.Options) (*coderd.API, error))
650646
// in-flight requests, give in-flight requests 5 seconds to
651647
// complete.
652648
cmd.Println("Shutting down API server...")
653-
err = shutdownWithTimeout(server.Shutdown, 5*time.Second)
649+
err = shutdownWithTimeout(server.Shutdown, 3*time.Second)
654650
if err != nil {
655-
cmd.Printf("API server shutdown took longer than 5s: %s", err)
651+
cmd.Printf("API server shutdown took longer than 3s: %s\n", err)
656652
} else {
657653
cmd.Printf("Gracefully shut down API server\n")
658654
}
@@ -694,10 +690,10 @@ func Server(newAPI func(context.Context, *coderd.Options) (*coderd.API, error))
694690
cmd.Println("Done waiting for WebSocket connections")
695691

696692
// Close tunnel after we no longer have in-flight connections.
697-
if tunnel {
693+
if tunnel != nil {
698694
cmd.Println("Waiting for tunnel to close...")
699695
closeTunnel()
700-
<-devTunnelErr
696+
<-tunnelErr
701697
cmd.Println("Done waiting for tunnel")
702698
}
703699

@@ -855,8 +851,6 @@ func Server(newAPI func(context.Context, *coderd.Options) (*coderd.API, error))
855851
"Paths to the private keys for each of the certificates. It requires a PEM-encoded file")
856852
cliflag.StringVarP(root.Flags(), &tlsMinVersion, "tls-min-version", "", "CODER_TLS_MIN_VERSION", "tls12",
857853
`Minimum supported version of TLS. Accepted values are "tls10", "tls11", "tls12" or "tls13"`)
858-
cliflag.BoolVarP(root.Flags(), &tunnel, "tunnel", "", "CODER_TUNNEL", false,
859-
"Workspaces must be able to reach the `access-url`. This overrides your access URL with a public access URL that tunnels your Coder deployment.")
860854
cliflag.BoolVarP(root.Flags(), &traceEnable, "trace", "", "CODER_TRACE", false,
861855
"Whether application tracing data is collected.")
862856
cliflag.BoolVarP(root.Flags(), &secureAuthCookie, "secure-auth-cookie", "", "CODER_SECURE_AUTH_COOKIE", false,

cli/server_test.go

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -56,6 +56,7 @@ func TestServer(t *testing.T) {
5656
root, cfg := clitest.New(t,
5757
"server",
5858
"--address", ":0",
59+
"--access-url", "example.com",
5960
"--postgres-url", connectionURL,
6061
"--cache-dir", t.TempDir(),
6162
)
@@ -87,6 +88,7 @@ func TestServer(t *testing.T) {
8788
root, cfg := clitest.New(t,
8889
"server",
8990
"--address", ":0",
91+
"--access-url", "example.com",
9092
"--cache-dir", t.TempDir(),
9193
)
9294
pty := ptytest.New(t)
@@ -127,6 +129,7 @@ func TestServer(t *testing.T) {
127129
"server",
128130
"--in-memory",
129131
"--address", ":0",
132+
"--access-url", "example.com",
130133
"--access-url", "localhost:3000/",
131134
"--cache-dir", t.TempDir(),
132135
)
@@ -159,6 +162,7 @@ func TestServer(t *testing.T) {
159162
"server",
160163
"--in-memory",
161164
"--address", ":0",
165+
"--access-url", "example.com",
162166
"--access-url", "foobarbaz.mydomain",
163167
"--cache-dir", t.TempDir(),
164168
)
@@ -189,6 +193,7 @@ func TestServer(t *testing.T) {
189193
"server",
190194
"--in-memory",
191195
"--address", ":0",
196+
"--access-url", "example.com",
192197
"--access-url", "https://google.com",
193198
"--cache-dir", t.TempDir(),
194199
)
@@ -218,6 +223,7 @@ func TestServer(t *testing.T) {
218223
"server",
219224
"--in-memory",
220225
"--address", ":0",
226+
"--access-url", "example.com",
221227
"--tls-enable",
222228
"--tls-min-version", "tls9",
223229
"--cache-dir", t.TempDir(),
@@ -234,6 +240,7 @@ func TestServer(t *testing.T) {
234240
"server",
235241
"--in-memory",
236242
"--address", ":0",
243+
"--access-url", "example.com",
237244
"--tls-enable",
238245
"--tls-client-auth", "something",
239246
"--cache-dir", t.TempDir(),
@@ -290,6 +297,7 @@ func TestServer(t *testing.T) {
290297
"server",
291298
"--in-memory",
292299
"--address", ":0",
300+
"--access-url", "example.com",
293301
"--cache-dir", t.TempDir(),
294302
}
295303
args = append(args, c.args...)
@@ -310,6 +318,7 @@ func TestServer(t *testing.T) {
310318
"server",
311319
"--in-memory",
312320
"--address", ":0",
321+
"--access-url", "example.com",
313322
"--tls-enable",
314323
"--tls-cert-file", certPath,
315324
"--tls-key-file", keyPath,
@@ -349,6 +358,7 @@ func TestServer(t *testing.T) {
349358
"server",
350359
"--in-memory",
351360
"--address", ":0",
361+
"--access-url", "example.com",
352362
"--tls-enable",
353363
"--tls-cert-file", cert1Path,
354364
"--tls-key-file", key1Path,
@@ -432,6 +442,7 @@ func TestServer(t *testing.T) {
432442
"server",
433443
"--in-memory",
434444
"--address", ":0",
445+
"--access-url", "example.com",
435446
"--provisioner-daemons", "1",
436447
"--cache-dir", t.TempDir(),
437448
)
@@ -458,6 +469,7 @@ func TestServer(t *testing.T) {
458469
"server",
459470
"--in-memory",
460471
"--address", ":0",
472+
"--access-url", "example.com",
461473
"--trace=true",
462474
"--cache-dir", t.TempDir(),
463475
)
@@ -495,6 +507,7 @@ func TestServer(t *testing.T) {
495507
"server",
496508
"--in-memory",
497509
"--address", ":0",
510+
"--access-url", "example.com",
498511
"--telemetry",
499512
"--telemetry-url", server.URL,
500513
"--cache-dir", t.TempDir(),
@@ -525,6 +538,7 @@ func TestServer(t *testing.T) {
525538
"server",
526539
"--in-memory",
527540
"--address", ":0",
541+
"--access-url", "example.com",
528542
"--provisioner-daemons", "1",
529543
"--prometheus-enable",
530544
"--prometheus-address", ":"+strconv.Itoa(randomPort),
@@ -577,6 +591,7 @@ func TestServer(t *testing.T) {
577591
"server",
578592
"--in-memory",
579593
"--address", ":0",
594+
"--access-url", "example.com",
580595
"--oauth2-github-client-id", "fake",
581596
"--oauth2-github-client-secret", "fake",
582597
"--oauth2-github-enterprise-base-url", fakeRedirect,

coder.env

Lines changed: 1 addition & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,4 @@
1-
# Coder must be reachable from an external URL
2-
# for users and workspaces to connect.
3-
4-
# Option 1) Enable CODER_TUNNEL to generate a
5-
# unique *. try.coder.com access URL
6-
CODER_TUNNEL=false
7-
8-
# Option 2) Set an access URL
1+
# Coder must be reachable from an external URL for users and workspaces to connect.
92
# e.g. https://coder.example.com
103
CODER_ACCESS_URL=
114

coderd/authorize.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -119,7 +119,7 @@ func (api *API) checkAuthorization(rw http.ResponseWriter, r *http.Request) {
119119
return
120120
}
121121

122-
api.Logger.Warn(ctx, "check-auth",
122+
api.Logger.Debug(ctx, "check-auth",
123123
slog.F("my_id", httpmw.APIKey(r).UserID),
124124
slog.F("got_id", auth.ID),
125125
slog.F("name", auth.Username),

coderd/csp.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,7 @@ func (api *API) logReportCSPViolations(rw http.ResponseWriter, r *http.Request)
3434
for k, v := range v.Report {
3535
fields = append(fields, slog.F(k, v))
3636
}
37-
api.Logger.Warn(ctx, "csp violation", fields...)
37+
api.Logger.Debug(ctx, "csp violation", fields...)
3838

3939
httpapi.Write(ctx, rw, http.StatusOK, "ok")
4040
}

coderd/workspaceagents.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -311,7 +311,7 @@ func (api *API) dialWorkspaceAgentTailnet(r *http.Request, agentID uuid.UUID) (*
311311
conn, err := tailnet.NewConn(&tailnet.Options{
312312
Addresses: []netip.Prefix{netip.PrefixFrom(tailnet.IP(), 128)},
313313
DERPMap: derpMap,
314-
Logger: api.Logger.Named("tailnet").Leveled(slog.LevelDebug),
314+
Logger: api.Logger.Named("tailnet"),
315315
})
316316
if err != nil {
317317
return nil, xerrors.Errorf("create tailnet conn: %w", err)
@@ -453,7 +453,7 @@ func (api *API) workspaceAgentCoordinate(rw http.ResponseWriter, r *http.Request
453453
// Ignore all trace spans after this.
454454
ctx = trace.ContextWithSpan(ctx, tracing.NoopSpan)
455455

456-
api.Logger.Info(ctx, "accepting agent", slog.F("resource", resource), slog.F("agent", workspaceAgent))
456+
api.Logger.Info(ctx, "accepting agent", slog.F("agent", workspaceAgent))
457457

458458
defer conn.Close(websocket.StatusNormalClosure, "")
459459

docker-compose.yaml

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -13,9 +13,6 @@ services:
1313
# that workspaces can reach. This cannot be localhost
1414
# or 127.0.0.1 for non-Docker templates!
1515
CODER_ACCESS_URL: "${CODER_ACCESS_URL}"
16-
# Alternatively, you can enable CODER_TUNNEL for
17-
# proof-of-concept deployments.
18-
CODER_TUNNEL: "${CODER_TUNNEL:-false}"
1916
# If the coder user does not have write permissions on
2017
# the docker socket, you can uncomment the following
2118
# lines and set the group ID to one that has write

docs/admin/configure.md

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,6 @@
11
Coder server's primary configuration is done via environment variables. For a full list
22
of the options, run `coder server --help` on the host.
33

4-
## Tunnel
5-
6-
For proof-of-concept deployments, you can set `CODER_TUNNEL=true` to run Coder on a unique `*.try.coder.app` URL.
7-
This is a quick way to allow users and workspaces outside your LAN to connect to Coder.
8-
94
## Access URL
105

116
`CODER_ACCESS_URL` is required if you are not using the tunnel. Set this to the external URL
@@ -14,6 +9,11 @@ should not be localhost.
149

1510
> Access URL should be a external IP address or domain with DNS records pointing to Coder.
1611
12+
### Tunnel
13+
14+
If an access URL is not specified, Coder will create
15+
a publicly accessible URL to reverse proxy your deployment for simple setup.
16+
1717
## Wildcard access URL
1818

1919
`CODER_WILDCARD_ACCESS_URL` is necessary for [port forwarding](../networking/port-forwarding.md#dashboard)

docs/images/quickstart/aws/aws7.png

-53.1 KB
Binary file not shown.
-53.1 KB
Binary file not shown.

docs/install/binary.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ Coder publishes self-contained .zip and .tar.gz archives in [GitHub releases](ht
1515

1616
```sh
1717
# Automatically sets up an external access URL on *.try.coder.app
18-
coder server --tunnel
18+
coder server
1919

2020
# Requires a PostgreSQL instance and external access URL
2121
coder server --postgres-url <url> --access-url <url>

docs/install/docker.md

Lines changed: 2 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ You can install and run Coder using the official Docker images published on [Git
44

55
Docker is required. See the [official installation documentation](https://docs.docker.com/install/).
66

7-
## Run Coder with built-in database and tunnel (quick)
7+
## Run Coder with the built-in database (quick)
88

99
For proof-of-concept deployments, you can run a complete Coder instance with
1010
the following command:
@@ -14,7 +14,6 @@ export CODER_DATA=$HOME/.config/coderv2-docker
1414
export DOCKER_GROUP=$(getent group docker | cut -d: -f3)
1515
mkdir -p $CODER_DATA
1616
docker run --rm -it \
17-
-e CODER_TUNNEL=true \
1817
-v $CODER_DATA:/home/coder/.config \
1918
-v /var/run/docker.sock:/var/run/docker.sock \
2019
--group-add $DOCKER_GROUP \
@@ -68,7 +67,7 @@ an PostgreSQL container and volume.
6867
```sh
6968
cd coder
7069

71-
CODER_TUNNEL=true docker-compose up
70+
docker-compose up
7271
```
7372

7473
For production deployments, we recommend setting an [access URL](../admin/configure.md#access-url):
@@ -79,8 +78,6 @@ an PostgreSQL container and volume.
7978
CODER_ACCESS_URL=https://coder.example.com docker-compose up
8079
```
8180

82-
> Without `CODER_ACCESS_URL` or `CODER_TUNNEL` set, Coder will bind to `localhost:7080`. This will only work for Docker-based templates.
83-
8481
4. Visit the web ui via the configured url. You can add `/login` to the base url to create the first user via the ui.
8582

8683
5. Follow the on-screen instructions log in and create your first template and workspace

docs/install/packages.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@
77
1. Run Coder as a system service.
88

99
```sh
10-
# Set up an access URL or enable CODER_TUNNEL
10+
# Set up an access URL
1111
sudo vim /etc/coder.d/coder.env
1212

1313
# To systemd to start Coder now and on reboot

0 commit comments

Comments
 (0)