Skip to content

Commit 3cc77d9

Browse files
kylecarbsbpmct
andauthored
feat: Add tunnel by default (coder#4399)
* 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. * Update install.sh Co-authored-by: Ben Potter <ben@coder.com> * Update docs/install/packages.md Co-authored-by: Ben Potter <ben@coder.com> * Fix reset pass test * Fix e2e test Co-authored-by: Ben Potter <ben@coder.com>
1 parent 3049a56 commit 3cc77d9

22 files changed

+60
-88
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/resetpassword_test.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,7 @@ func TestResetPassword(t *testing.T) {
4141
serverCmd, cfg := clitest.New(t,
4242
"server",
4343
"--address", ":0",
44+
"--access-url", "example.com",
4445
"--postgres-url", connectionURL,
4546
"--cache-dir", t.TempDir(),
4647
)

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: 14 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)
@@ -159,6 +161,7 @@ func TestServer(t *testing.T) {
159161
"server",
160162
"--in-memory",
161163
"--address", ":0",
164+
"--access-url", "example.com",
162165
"--access-url", "foobarbaz.mydomain",
163166
"--cache-dir", t.TempDir(),
164167
)
@@ -189,6 +192,7 @@ func TestServer(t *testing.T) {
189192
"server",
190193
"--in-memory",
191194
"--address", ":0",
195+
"--access-url", "example.com",
192196
"--access-url", "https://google.com",
193197
"--cache-dir", t.TempDir(),
194198
)
@@ -218,6 +222,7 @@ func TestServer(t *testing.T) {
218222
"server",
219223
"--in-memory",
220224
"--address", ":0",
225+
"--access-url", "example.com",
221226
"--tls-enable",
222227
"--tls-min-version", "tls9",
223228
"--cache-dir", t.TempDir(),
@@ -234,6 +239,7 @@ func TestServer(t *testing.T) {
234239
"server",
235240
"--in-memory",
236241
"--address", ":0",
242+
"--access-url", "example.com",
237243
"--tls-enable",
238244
"--tls-client-auth", "something",
239245
"--cache-dir", t.TempDir(),
@@ -290,6 +296,7 @@ func TestServer(t *testing.T) {
290296
"server",
291297
"--in-memory",
292298
"--address", ":0",
299+
"--access-url", "example.com",
293300
"--cache-dir", t.TempDir(),
294301
}
295302
args = append(args, c.args...)
@@ -310,6 +317,7 @@ func TestServer(t *testing.T) {
310317
"server",
311318
"--in-memory",
312319
"--address", ":0",
320+
"--access-url", "example.com",
313321
"--tls-enable",
314322
"--tls-cert-file", certPath,
315323
"--tls-key-file", keyPath,
@@ -349,6 +357,7 @@ func TestServer(t *testing.T) {
349357
"server",
350358
"--in-memory",
351359
"--address", ":0",
360+
"--access-url", "example.com",
352361
"--tls-enable",
353362
"--tls-cert-file", cert1Path,
354363
"--tls-key-file", key1Path,
@@ -432,6 +441,7 @@ func TestServer(t *testing.T) {
432441
"server",
433442
"--in-memory",
434443
"--address", ":0",
444+
"--access-url", "example.com",
435445
"--provisioner-daemons", "1",
436446
"--cache-dir", t.TempDir(),
437447
)
@@ -458,6 +468,7 @@ func TestServer(t *testing.T) {
458468
"server",
459469
"--in-memory",
460470
"--address", ":0",
471+
"--access-url", "example.com",
461472
"--trace=true",
462473
"--cache-dir", t.TempDir(),
463474
)
@@ -495,6 +506,7 @@ func TestServer(t *testing.T) {
495506
"server",
496507
"--in-memory",
497508
"--address", ":0",
509+
"--access-url", "example.com",
498510
"--telemetry",
499511
"--telemetry-url", server.URL,
500512
"--cache-dir", t.TempDir(),
@@ -525,6 +537,7 @@ func TestServer(t *testing.T) {
525537
"server",
526538
"--in-memory",
527539
"--address", ":0",
540+
"--access-url", "example.com",
528541
"--provisioner-daemons", "1",
529542
"--prometheus-enable",
530543
"--prometheus-address", ":"+strconv.Itoa(randomPort),
@@ -577,6 +590,7 @@ func TestServer(t *testing.T) {
577590
"server",
578591
"--in-memory",
579592
"--address", ":0",
593+
"--access-url", "example.com",
580594
"--oauth2-github-client-id", "fake",
581595
"--oauth2-github-client-secret", "fake",
582596
"--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+
# Optional) 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)