feat: allow http and https listening simultaneously (#5365)

This commit is contained in:
Dean Sheather
2022-12-15 20:09:19 +00:00
committed by GitHub
parent 787b8b2a51
commit 31d38d4246
15 changed files with 692 additions and 136 deletions
+23 -1
View File
@@ -32,12 +32,22 @@ func newConfig() *codersdk.DeploymentConfig {
Usage: "Specifies the wildcard hostname to use for workspace applications in the form \"*.example.com\".",
Flag: "wildcard-access-url",
},
// DEPRECATED: Use HTTPAddress or TLS.Address instead.
Address: &codersdk.DeploymentConfigField[string]{
Name: "Address",
Usage: "Bind address of the server.",
Flag: "address",
Shorthand: "a",
Default: "127.0.0.1:3000",
// Deprecated, so we don't have a default. If set, it will overwrite
// HTTPAddress and TLS.Address and print a warning.
Hidden: true,
Default: "",
},
HTTPAddress: &codersdk.DeploymentConfigField[string]{
Name: "Address",
Usage: "HTTP bind address of the server. Unset to disable the HTTP endpoint.",
Flag: "http-address",
Default: "127.0.0.1:3000",
},
AutobuildPollInterval: &codersdk.DeploymentConfigField[time.Duration]{
Name: "Autobuild Poll Interval",
@@ -267,6 +277,18 @@ func newConfig() *codersdk.DeploymentConfig {
Usage: "Whether TLS will be enabled.",
Flag: "tls-enable",
},
Address: &codersdk.DeploymentConfigField[string]{
Name: "TLS Address",
Usage: "HTTPS bind address of the server.",
Flag: "tls-address",
Default: "127.0.0.1:3443",
},
RedirectHTTP: &codersdk.DeploymentConfigField[bool]{
Name: "Redirect HTTP to HTTPS",
Usage: "Whether HTTP requests will be redirected to the access URL (if it's a https URL and TLS is enabled). Requests to local IP addresses are never redirected regardless of this setting.",
Flag: "tls-redirect-http-to-https",
Default: true,
},
CertFiles: &codersdk.DeploymentConfigField[[]string]{
Name: "TLS Certificate Files",
Usage: "Path to each certificate for TLS. It requires a PEM-encoded file. To configure the listener to use a CA certificate, concatenate the primary certificate and the CA certificate together. The primary certificate should appear first in the combined file.",
+1 -1
View File
@@ -40,7 +40,7 @@ func TestResetPassword(t *testing.T) {
serverDone := make(chan struct{})
serverCmd, cfg := clitest.New(t,
"server",
"--address", ":0",
"--http-address", ":0",
"--access-url", "http://example.com",
"--postgres-url", connectionURL,
"--cache-dir", t.TempDir(),
+153 -49
View File
@@ -85,6 +85,25 @@ func Server(vip *viper.Viper, newAPI func(context.Context, *coderd.Options) (*co
if err != nil {
return xerrors.Errorf("getting deployment config: %w", err)
}
// Validate bind addresses.
if cfg.Address.Value != "" {
cmd.PrintErr(cliui.Styles.Warn.Render("WARN:") + " --address and -a are deprecated, please use --http-address and --tls-address instead")
if cfg.TLS.Enable.Value {
cfg.HTTPAddress.Value = ""
cfg.TLS.Address.Value = cfg.Address.Value
} else {
cfg.HTTPAddress.Value = cfg.Address.Value
cfg.TLS.Address.Value = ""
}
}
if cfg.TLS.Enable.Value && cfg.TLS.Address.Value == "" {
return xerrors.Errorf("TLS address must be set if TLS is enabled")
}
if !cfg.TLS.Enable.Value && cfg.HTTPAddress.Value == "" {
return xerrors.Errorf("either HTTP or TLS must be enabled")
}
printLogo(cmd)
logger := slog.Make(sloghuman.Sink(cmd.ErrOrStderr()))
if ok, _ := cmd.Flags().GetBool(varVerbose); ok {
@@ -186,14 +205,41 @@ func Server(vip *viper.Viper, newAPI func(context.Context, *coderd.Options) (*co
}()
}
listener, err := net.Listen("tcp", cfg.Address.Value)
if err != nil {
return xerrors.Errorf("listen %q: %w", cfg.Address.Value, err)
}
defer listener.Close()
var (
httpListener net.Listener
httpURL *url.URL
)
if cfg.HTTPAddress.Value != "" {
httpListener, err = net.Listen("tcp", cfg.HTTPAddress.Value)
if err != nil {
return xerrors.Errorf("listen %q: %w", cfg.HTTPAddress.Value, err)
}
defer httpListener.Close()
var tlsConfig *tls.Config
tcpAddr, tcpAddrValid := httpListener.Addr().(*net.TCPAddr)
if !tcpAddrValid {
return xerrors.Errorf("invalid TCP address type %T", httpListener.Addr())
}
if tcpAddr.IP.IsUnspecified() {
tcpAddr.IP = net.IPv4(127, 0, 0, 1)
}
httpURL = &url.URL{
Scheme: "http",
Host: tcpAddr.String(),
}
cmd.Println("Started HTTP listener at " + httpURL.String())
}
var (
tlsConfig *tls.Config
httpsListener net.Listener
httpsURL *url.URL
)
if cfg.TLS.Enable.Value {
if cfg.TLS.Address.Value == "" {
return xerrors.New("tls address must be set if tls is enabled")
}
tlsConfig, err = configureTLS(
cfg.TLS.MinVersion.Value,
cfg.TLS.ClientAuth.Value,
@@ -204,7 +250,38 @@ func Server(vip *viper.Viper, newAPI func(context.Context, *coderd.Options) (*co
if err != nil {
return xerrors.Errorf("configure tls: %w", err)
}
listener = tls.NewListener(listener, tlsConfig)
httpsListenerInner, err := net.Listen("tcp", cfg.TLS.Address.Value)
if err != nil {
return xerrors.Errorf("listen %q: %w", cfg.TLS.Address.Value, err)
}
defer httpsListenerInner.Close()
httpsListener = tls.NewListener(httpsListenerInner, tlsConfig)
defer httpsListener.Close()
tcpAddr, tcpAddrValid := httpsListener.Addr().(*net.TCPAddr)
if !tcpAddrValid {
return xerrors.Errorf("invalid TCP address type %T", httpsListener.Addr())
}
if tcpAddr.IP.IsUnspecified() {
tcpAddr.IP = net.IPv4(127, 0, 0, 1)
}
httpsURL = &url.URL{
Scheme: "https",
Host: tcpAddr.String(),
}
cmd.Println("Started TLS/HTTPS listener at " + httpsURL.String())
}
// Sanity check that at least one listener was started.
if httpListener == nil && httpsListener == nil {
return xerrors.New("must listen on at least one address")
}
// Prefer HTTP because it's less prone to TLS errors over localhost.
localURL := httpsURL
if httpURL != nil {
localURL = httpURL
}
ctx, httpClient, err := configureHTTPClient(
@@ -217,24 +294,6 @@ func Server(vip *viper.Viper, newAPI func(context.Context, *coderd.Options) (*co
return xerrors.Errorf("configure http client: %w", err)
}
tcpAddr, valid := listener.Addr().(*net.TCPAddr)
if !valid {
return xerrors.New("must be listening on tcp")
}
// If just a port is specified, assume localhost.
if tcpAddr.IP.IsUnspecified() {
tcpAddr.IP = net.IPv4(127, 0, 0, 1)
}
// If no access URL is specified, fallback to the
// bounds URL.
localURL := &url.URL{
Scheme: "http",
Host: tcpAddr.String(),
}
if cfg.TLS.Enable.Value {
localURL.Scheme = "https"
}
var (
ctxTunnel, closeTunnel = context.WithCancel(ctx)
tunnel *devtunnel.Tunnel
@@ -289,6 +348,15 @@ func Server(vip *viper.Viper, newAPI func(context.Context, *coderd.Options) (*co
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)
}
// Redirect from the HTTP listener to the access URL if:
// 1. The redirect flag is enabled.
// 2. HTTP listening is enabled (obviously).
// 3. TLS is enabled (otherwise they're likely using a reverse proxy
// which can do this instead).
// 4. The access URL has been set manually (not a tunnel).
// 5. The access URL is HTTPS.
shouldRedirectHTTPToAccessURL := cfg.TLS.RedirectHTTP.Value && cfg.HTTPAddress.Value != "" && cfg.TLS.Enable.Value && tunnel == nil && accessURLParsed.Scheme == "https"
// A newline is added before for visibility in terminal output.
cmd.Printf("\nView the Web UI: %s\n", accessURLParsed.String())
@@ -630,6 +698,11 @@ func Server(vip *viper.Viper, newAPI func(context.Context, *coderd.Options) (*co
defer client.HTTPClient.CloseIdleConnections()
}
// This is helpful for tests, but can be silently ignored.
// Coder may be ran as users that don't have permission to write in the homedir,
// such as via the systemd service.
_ = config.URL().Write(client.URL.String())
// Since errCh only has one buffered slot, all routines
// sending on it must be wrapped in a select/default to
// avoid leaving dangling goroutines waiting for the
@@ -657,40 +730,65 @@ func Server(vip *viper.Viper, newAPI func(context.Context, *coderd.Options) (*co
shutdownConnsCtx, shutdownConns := context.WithCancel(ctx)
defer shutdownConns()
// ReadHeaderTimeout is purposefully not enabled. It caused some issues with
// websockets over the dev tunnel.
// Wrap the server in middleware that redirects to the access URL if
// the request is not to a local IP.
var handler http.Handler = coderAPI.RootHandler
if shouldRedirectHTTPToAccessURL {
handler = redirectHTTPToAccessURL(handler, accessURLParsed)
}
// ReadHeaderTimeout is purposefully not enabled. It caused some
// issues with websockets over the dev tunnel.
// See: https://github.com/coder/coder/pull/3730
//nolint:gosec
server := &http.Server{
// These errors are typically noise like "TLS: EOF". Vault does similar:
httpServer := &http.Server{
// These errors are typically noise like "TLS: EOF". Vault does
// similar:
// https://github.com/hashicorp/vault/blob/e2490059d0711635e529a4efcbaa1b26998d6e1c/command/server.go#L2714
ErrorLog: log.New(io.Discard, "", 0),
Handler: coderAPI.RootHandler,
Handler: handler,
BaseContext: func(_ net.Listener) context.Context {
return shutdownConnsCtx
},
}
defer func() {
_ = shutdownWithTimeout(server.Shutdown, 5*time.Second)
_ = shutdownWithTimeout(httpServer.Shutdown, 5*time.Second)
}()
eg := errgroup.Group{}
eg.Go(func() error {
// Make sure to close the tunnel listener if we exit so the
// errgroup doesn't wait forever!
if tunnel != nil {
defer tunnel.Listener.Close()
// We call this in the routine so we can kill the other listeners if
// one of them fails.
closeListenersNow := func() {
if httpListener != nil {
_ = httpListener.Close()
}
if httpsListener != nil {
_ = httpsListener.Close()
}
if tunnel != nil {
_ = tunnel.Listener.Close()
}
}
return server.Serve(listener)
})
if tunnel != nil {
eg := errgroup.Group{}
if httpListener != nil {
eg.Go(func() error {
defer listener.Close()
return server.Serve(tunnel.Listener)
defer closeListenersNow()
return httpServer.Serve(httpListener)
})
}
if httpsListener != nil {
eg.Go(func() error {
defer closeListenersNow()
return httpServer.Serve(httpsListener)
})
}
if tunnel != nil {
eg.Go(func() error {
defer closeListenersNow()
return httpServer.Serve(tunnel.Listener)
})
}
go func() {
select {
case errCh <- eg.Wait():
@@ -718,11 +816,6 @@ func Server(vip *viper.Viper, newAPI func(context.Context, *coderd.Options) (*co
autobuildExecutor := executor.New(ctx, options.Database, logger, autobuildPoller.C)
autobuildExecutor.Run()
// This is helpful for tests, but can be silently ignored.
// Coder may be ran as users that don't have permission to write in the homedir,
// such as via the systemd service.
_ = config.URL().Write(client.URL.String())
// Currently there is no way to ask the server to shut
// itself down, so any exit signal will result in a non-zero
// exit of the server.
@@ -759,7 +852,7 @@ func Server(vip *viper.Viper, newAPI func(context.Context, *coderd.Options) (*co
// in-flight requests, give in-flight requests 5 seconds to
// complete.
cmd.Println("Shutting down API server...")
err = shutdownWithTimeout(server.Shutdown, 3*time.Second)
err = shutdownWithTimeout(httpServer.Shutdown, 3*time.Second)
if err != nil {
cmd.Printf("API server shutdown took longer than 3s: %s\n", err)
} else {
@@ -1357,3 +1450,14 @@ func configureHTTPClient(ctx context.Context, clientCertFile, clientKeyFile stri
}
return ctx, &http.Client{}, nil
}
func redirectHTTPToAccessURL(handler http.Handler, accessURL *url.URL) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
if r.TLS == nil {
http.Redirect(w, r, accessURL.String(), http.StatusTemporaryRedirect)
return
}
handler.ServeHTTP(w, r)
})
}
+351 -19
View File
@@ -55,7 +55,7 @@ func TestServer(t *testing.T) {
root, cfg := clitest.New(t,
"server",
"--address", ":0",
"--http-address", ":0",
"--access-url", "http://example.com",
"--postgres-url", connectionURL,
"--cache-dir", t.TempDir(),
@@ -89,7 +89,7 @@ func TestServer(t *testing.T) {
root, cfg := clitest.New(t,
"server",
"--address", ":0",
"--http-address", ":0",
"--access-url", "http://example.com",
"--cache-dir", t.TempDir(),
)
@@ -129,7 +129,7 @@ func TestServer(t *testing.T) {
root, cfg := clitest.New(t,
"server",
"--in-memory",
"--address", ":0",
"--http-address", ":0",
"--access-url", "http://localhost:3000/",
"--cache-dir", t.TempDir(),
)
@@ -161,7 +161,7 @@ func TestServer(t *testing.T) {
root, cfg := clitest.New(t,
"server",
"--in-memory",
"--address", ":0",
"--http-address", ":0",
"--access-url", "https://foobarbaz.mydomain",
"--cache-dir", t.TempDir(),
)
@@ -191,7 +191,7 @@ func TestServer(t *testing.T) {
root, cfg := clitest.New(t,
"server",
"--in-memory",
"--address", ":0",
"--http-address", ":0",
"--access-url", "https://google.com",
"--cache-dir", t.TempDir(),
)
@@ -220,7 +220,7 @@ func TestServer(t *testing.T) {
root, _ := clitest.New(t,
"server",
"--in-memory",
"--address", ":0",
"--http-address", ":0",
"--access-url", "google.com",
"--cache-dir", t.TempDir(),
)
@@ -236,9 +236,10 @@ func TestServer(t *testing.T) {
root, _ := clitest.New(t,
"server",
"--in-memory",
"--address", ":0",
"--http-address", "",
"--access-url", "http://example.com",
"--tls-enable",
"--tls-address", ":0",
"--tls-min-version", "tls9",
"--cache-dir", t.TempDir(),
)
@@ -253,9 +254,10 @@ func TestServer(t *testing.T) {
root, _ := clitest.New(t,
"server",
"--in-memory",
"--address", ":0",
"--http-address", "",
"--access-url", "http://example.com",
"--tls-enable",
"--tls-address", ":0",
"--tls-client-auth", "something",
"--cache-dir", t.TempDir(),
)
@@ -310,7 +312,7 @@ func TestServer(t *testing.T) {
args := []string{
"server",
"--in-memory",
"--address", ":0",
"--http-address", ":0",
"--access-url", "http://example.com",
"--cache-dir", t.TempDir(),
}
@@ -331,9 +333,10 @@ func TestServer(t *testing.T) {
root, cfg := clitest.New(t,
"server",
"--in-memory",
"--address", ":0",
"--http-address", "",
"--access-url", "http://example.com",
"--tls-enable",
"--tls-address", ":0",
"--tls-cert-file", certPath,
"--tls-key-file", keyPath,
"--cache-dir", t.TempDir(),
@@ -371,9 +374,10 @@ func TestServer(t *testing.T) {
root, cfg := clitest.New(t,
"server",
"--in-memory",
"--address", ":0",
"--http-address", "",
"--access-url", "http://example.com",
"--tls-enable",
"--tls-address", ":0",
"--tls-cert-file", cert1Path,
"--tls-key-file", key1Path,
"--tls-cert-file", cert2Path,
@@ -443,6 +447,334 @@ func TestServer(t *testing.T) {
cancelFunc()
require.NoError(t, <-errC)
})
t.Run("TLSAndHTTP", func(t *testing.T) {
t.Parallel()
ctx, cancelFunc := context.WithCancel(context.Background())
defer cancelFunc()
certPath, keyPath := generateTLSCertificate(t)
root, _ := clitest.New(t,
"server",
"--in-memory",
"--http-address", ":0",
"--access-url", "https://example.com",
"--tls-enable",
"--tls-redirect-http-to-https=false",
"--tls-address", ":0",
"--tls-cert-file", certPath,
"--tls-key-file", keyPath,
"--cache-dir", t.TempDir(),
)
pty := ptytest.New(t)
root.SetOutput(pty.Output())
root.SetErr(pty.Output())
errC := make(chan error, 1)
go func() {
errC <- root.ExecuteContext(ctx)
}()
// We can't use waitAccessURL as it will only return the HTTP URL.
const httpLinePrefix = "Started HTTP listener at "
pty.ExpectMatch(httpLinePrefix)
httpLine := pty.ReadLine()
httpAddr := strings.TrimSpace(strings.TrimPrefix(httpLine, httpLinePrefix))
require.NotEmpty(t, httpAddr)
const tlsLinePrefix = "Started TLS/HTTPS listener at "
pty.ExpectMatch(tlsLinePrefix)
tlsLine := pty.ReadLine()
tlsAddr := strings.TrimSpace(strings.TrimPrefix(tlsLine, tlsLinePrefix))
require.NotEmpty(t, tlsAddr)
// Verify HTTP
httpURL, err := url.Parse(httpAddr)
require.NoError(t, err)
client := codersdk.New(httpURL)
client.HTTPClient.CheckRedirect = func(req *http.Request, via []*http.Request) error {
return http.ErrUseLastResponse
}
_, err = client.HasFirstUser(ctx)
require.NoError(t, err)
// Verify TLS
tlsURL, err := url.Parse(tlsAddr)
require.NoError(t, err)
client = codersdk.New(tlsURL)
client.HTTPClient.CheckRedirect = func(req *http.Request, via []*http.Request) error {
return http.ErrUseLastResponse
}
client.HTTPClient = &http.Client{
Transport: &http.Transport{
TLSClientConfig: &tls.Config{
//nolint:gosec
InsecureSkipVerify: true,
},
},
}
_, err = client.HasFirstUser(ctx)
require.NoError(t, err)
cancelFunc()
require.NoError(t, <-errC)
})
t.Run("TLSRedirect", func(t *testing.T) {
t.Parallel()
cases := []struct {
name string
httpListener bool
tlsListener bool
accessURL string
// Empty string means no redirect.
expectRedirect string
}{
{
name: "OK",
httpListener: true,
tlsListener: true,
accessURL: "https://example.com",
expectRedirect: "https://example.com",
},
{
name: "NoTLSListener",
httpListener: true,
tlsListener: false,
accessURL: "https://example.com",
expectRedirect: "",
},
{
name: "NoHTTPListener",
httpListener: false,
tlsListener: true,
accessURL: "https://example.com",
expectRedirect: "",
},
}
for _, c := range cases {
c := c
t.Run(c.name, func(t *testing.T) {
t.Parallel()
ctx, cancelFunc := context.WithCancel(context.Background())
defer cancelFunc()
certPath, keyPath := generateTLSCertificate(t)
flags := []string{
"server",
"--in-memory",
"--cache-dir", t.TempDir(),
}
if c.httpListener {
flags = append(flags, "--http-address", ":0")
}
if c.tlsListener {
flags = append(flags,
"--tls-enable",
"--tls-address", ":0",
"--tls-cert-file", certPath,
"--tls-key-file", keyPath,
)
}
if c.accessURL != "" {
flags = append(flags, "--access-url", c.accessURL)
}
root, _ := clitest.New(t, flags...)
pty := ptytest.New(t)
root.SetOutput(pty.Output())
root.SetErr(pty.Output())
errC := make(chan error, 1)
go func() {
errC <- root.ExecuteContext(ctx)
}()
var (
httpAddr string
tlsAddr string
)
// We can't use waitAccessURL as it will only return the HTTP URL.
if c.httpListener {
const httpLinePrefix = "Started HTTP listener at "
pty.ExpectMatch(httpLinePrefix)
httpLine := pty.ReadLine()
httpAddr = strings.TrimSpace(strings.TrimPrefix(httpLine, httpLinePrefix))
require.NotEmpty(t, httpAddr)
}
if c.tlsListener {
const tlsLinePrefix = "Started TLS/HTTPS listener at "
pty.ExpectMatch(tlsLinePrefix)
tlsLine := pty.ReadLine()
tlsAddr = strings.TrimSpace(strings.TrimPrefix(tlsLine, tlsLinePrefix))
require.NotEmpty(t, tlsAddr)
}
// Verify HTTP redirects (or not)
if c.httpListener {
httpURL, err := url.Parse(httpAddr)
require.NoError(t, err)
client := codersdk.New(httpURL)
client.HTTPClient.CheckRedirect = func(req *http.Request, via []*http.Request) error {
return http.ErrUseLastResponse
}
resp, err := client.Request(ctx, http.MethodGet, "/api/v2/buildinfo", nil)
require.NoError(t, err)
defer resp.Body.Close()
if c.expectRedirect == "" {
require.Equal(t, http.StatusOK, resp.StatusCode)
} else {
require.Equal(t, http.StatusTemporaryRedirect, resp.StatusCode)
require.Equal(t, c.expectRedirect, resp.Header.Get("Location"))
}
}
// Verify TLS
if c.tlsListener {
tlsURL, err := url.Parse(tlsAddr)
require.NoError(t, err)
client := codersdk.New(tlsURL)
client.HTTPClient = &http.Client{
CheckRedirect: func(req *http.Request, via []*http.Request) error {
return http.ErrUseLastResponse
},
Transport: &http.Transport{
TLSClientConfig: &tls.Config{
//nolint:gosec
InsecureSkipVerify: true,
},
},
}
_, err = client.HasFirstUser(ctx)
require.NoError(t, err)
cancelFunc()
require.NoError(t, <-errC)
}
})
}
})
t.Run("NoAddress", func(t *testing.T) {
t.Parallel()
ctx, cancelFunc := context.WithCancel(context.Background())
defer cancelFunc()
root, _ := clitest.New(t,
"server",
"--in-memory",
"--http-address", "",
"--tls-enable=false",
"--tls-address", "",
)
err := root.ExecuteContext(ctx)
require.Error(t, err)
require.ErrorContains(t, err, "either HTTP or TLS must be enabled")
})
t.Run("NoTLSAddress", func(t *testing.T) {
t.Parallel()
ctx, cancelFunc := context.WithCancel(context.Background())
defer cancelFunc()
root, _ := clitest.New(t,
"server",
"--in-memory",
"--tls-enable=true",
"--tls-address", "",
)
err := root.ExecuteContext(ctx)
require.Error(t, err)
require.ErrorContains(t, err, "TLS address must be set if TLS is enabled")
})
// DeprecatedAddress is a test for the deprecated --address flag. If
// specified, --http-address and --tls-address are both ignored, a warning
// is printed, and the server will either be HTTP-only or TLS-only depending
// on if --tls-enable is set.
t.Run("DeprecatedAddress", func(t *testing.T) {
t.Parallel()
t.Run("HTTP", func(t *testing.T) {
t.Parallel()
ctx, cancelFunc := context.WithCancel(context.Background())
defer cancelFunc()
root, cfg := clitest.New(t,
"server",
"--in-memory",
"--address", ":0",
"--access-url", "http://example.com",
"--cache-dir", t.TempDir(),
)
pty := ptytest.New(t)
root.SetOutput(pty.Output())
root.SetErr(pty.Output())
errC := make(chan error, 1)
go func() {
errC <- root.ExecuteContext(ctx)
}()
pty.ExpectMatch("--address and -a are deprecated")
accessURL := waitAccessURL(t, cfg)
require.Equal(t, "http", accessURL.Scheme)
client := codersdk.New(accessURL)
_, err := client.HasFirstUser(ctx)
require.NoError(t, err)
cancelFunc()
require.NoError(t, <-errC)
})
t.Run("TLS", func(t *testing.T) {
t.Parallel()
ctx, cancelFunc := context.WithCancel(context.Background())
defer cancelFunc()
certPath, keyPath := generateTLSCertificate(t)
root, cfg := clitest.New(t,
"server",
"--in-memory",
"--address", ":0",
"--access-url", "http://example.com",
"--tls-enable",
"--tls-cert-file", certPath,
"--tls-key-file", keyPath,
"--cache-dir", t.TempDir(),
)
pty := ptytest.New(t)
root.SetOutput(pty.Output())
root.SetErr(pty.Output())
errC := make(chan error, 1)
go func() {
errC <- root.ExecuteContext(ctx)
}()
pty.ExpectMatch("--address and -a are deprecated")
accessURL := waitAccessURL(t, cfg)
require.Equal(t, "https", accessURL.Scheme)
client := codersdk.New(accessURL)
client.HTTPClient = &http.Client{
Transport: &http.Transport{
TLSClientConfig: &tls.Config{
//nolint:gosec
InsecureSkipVerify: true,
},
},
}
_, err := client.HasFirstUser(ctx)
require.NoError(t, err)
cancelFunc()
require.NoError(t, <-errC)
})
})
// This cannot be ran in parallel because it uses a signal.
//nolint:paralleltest
t.Run("Shutdown", func(t *testing.T) {
@@ -456,7 +788,7 @@ func TestServer(t *testing.T) {
root, cfg := clitest.New(t,
"server",
"--in-memory",
"--address", ":0",
"--http-address", ":0",
"--access-url", "http://example.com",
"--provisioner-daemons", "1",
"--cache-dir", t.TempDir(),
@@ -483,7 +815,7 @@ func TestServer(t *testing.T) {
root, _ := clitest.New(t,
"server",
"--in-memory",
"--address", ":0",
"--http-address", ":0",
"--access-url", "http://example.com",
"--trace=true",
"--cache-dir", t.TempDir(),
@@ -521,7 +853,7 @@ func TestServer(t *testing.T) {
root, _ := clitest.New(t,
"server",
"--in-memory",
"--address", ":0",
"--http-address", ":0",
"--access-url", "http://example.com",
"--telemetry",
"--telemetry-url", server.URL,
@@ -552,7 +884,7 @@ func TestServer(t *testing.T) {
root, cfg := clitest.New(t,
"server",
"--in-memory",
"--address", ":0",
"--http-address", ":0",
"--access-url", "http://example.com",
"--provisioner-daemons", "1",
"--prometheus-enable",
@@ -605,7 +937,7 @@ func TestServer(t *testing.T) {
root, cfg := clitest.New(t,
"server",
"--in-memory",
"--address", ":0",
"--http-address", ":0",
"--access-url", "http://example.com",
"--oauth2-github-allow-everyone",
"--oauth2-github-client-id", "fake",
@@ -646,7 +978,7 @@ func TestServer(t *testing.T) {
root, cfg := clitest.New(t,
"server",
"--in-memory",
"--address", ":0",
"--http-address", ":0",
"--access-url", "http://example.com",
)
serverErr := make(chan error, 1)
@@ -674,7 +1006,7 @@ func TestServer(t *testing.T) {
root, cfg := clitest.New(t,
"server",
"--in-memory",
"--address", ":0",
"--http-address", ":0",
"--access-url", "http://example.com",
"--api-rate-limit", val,
)
@@ -702,7 +1034,7 @@ func TestServer(t *testing.T) {
root, cfg := clitest.New(t,
"server",
"--in-memory",
"--address", ":0",
"--http-address", ":0",
"--access-url", "http://example.com",
"--api-rate-limit", "-1",
)
+14 -3
View File
@@ -14,9 +14,6 @@ Flags:
This must be accessible by all
provisioned workspaces.
Consumes $CODER_ACCESS_URL
-a, --address string Bind address of the server.
Consumes $CODER_ADDRESS (default
"127.0.0.1:3000")
--api-rate-limit int Maximum number of requests per minute
allowed to the API per user, or per IP
address for unauthenticated users.
@@ -65,6 +62,10 @@ Flags:
production.
Consumes $CODER_EXPERIMENTAL
-h, --help help for server
--http-address string HTTP bind address of the server. Unset to
disable the HTTP endpoint.
Consumes $CODER_HTTP_ADDRESS (default
"127.0.0.1:3000")
--max-token-lifetime duration The maximum lifetime duration for any
user creating a token.
Consumes $CODER_MAX_TOKEN_LIFETIME
@@ -178,6 +179,9 @@ Flags:
product. Disabling telemetry also
disables this option.
Consumes $CODER_TELEMETRY_TRACE
--tls-address string HTTPS bind address of the server.
Consumes $CODER_TLS_ADDRESS (default
"127.0.0.1:3443")
--tls-cert-file strings Path to each certificate for TLS. It
requires a PEM-encoded file. To configure
the listener to use a CA certificate,
@@ -216,6 +220,13 @@ Flags:
"tls12" or "tls13"
Consumes $CODER_TLS_MIN_VERSION (default
"tls12")
--tls-redirect-http-to-https Whether HTTP requests will be redirected
to the access URL (if it's a https URL
and TLS is enabled). Requests to local IP
addresses are never redirected regardless
of this setting.
Consumes $CODER_TLS_REDIRECT_HTTP
(default true)
--trace Whether application tracing data is
collected. It exports to a backend
configured by environment variables. See: