mirror of
https://github.com/coder/coder.git
synced 2026-09-24 15:04:27 +08:00
feat: add --header-command flag (#9059)
This allows specifying a command to run that can output headers for cases where users require dynamic headers (like to authenticate to their VPN). The primary use case is to add this flag in SSH configs created by the VS Code plugin, although maybe config-ssh should do the same.
This commit is contained in:
+43
-7
@@ -1,6 +1,8 @@
|
||||
package cli
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"bytes"
|
||||
"context"
|
||||
"encoding/base64"
|
||||
"encoding/json"
|
||||
@@ -13,6 +15,7 @@ import (
|
||||
"net/http"
|
||||
"net/url"
|
||||
"os"
|
||||
"os/exec"
|
||||
"os/signal"
|
||||
"path/filepath"
|
||||
"runtime"
|
||||
@@ -55,6 +58,7 @@ const (
|
||||
varAgentToken = "agent-token"
|
||||
varAgentURL = "agent-url"
|
||||
varHeader = "header"
|
||||
varHeaderCommand = "header-command"
|
||||
varNoOpen = "no-open"
|
||||
varNoVersionCheck = "no-version-warning"
|
||||
varNoFeatureWarning = "no-feature-warning"
|
||||
@@ -356,6 +360,13 @@ func (r *RootCmd) Command(subcommands []*clibase.Cmd) (*clibase.Cmd, error) {
|
||||
Value: clibase.StringArrayOf(&r.header),
|
||||
Group: globalGroup,
|
||||
},
|
||||
{
|
||||
Flag: varHeaderCommand,
|
||||
Env: "CODER_HEADER_COMMAND",
|
||||
Description: "An external command that outputs additional HTTP headers added to all requests. The command must output each header as `key=value` on its own line.",
|
||||
Value: clibase.StringOf(&r.headerCommand),
|
||||
Group: globalGroup,
|
||||
},
|
||||
{
|
||||
Flag: varNoOpen,
|
||||
Env: "CODER_NO_OPEN",
|
||||
@@ -437,6 +448,7 @@ type RootCmd struct {
|
||||
token string
|
||||
globalConfig string
|
||||
header []string
|
||||
headerCommand string
|
||||
agentToken string
|
||||
agentURL *url.URL
|
||||
forceTTY bool
|
||||
@@ -540,9 +552,7 @@ func (r *RootCmd) initClientInternal(client *codersdk.Client, allowTokenMissing
|
||||
return err
|
||||
}
|
||||
}
|
||||
err = r.setClient(
|
||||
client, r.clientURL,
|
||||
)
|
||||
err = r.setClient(inv.Context(), client, r.clientURL)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -592,12 +602,38 @@ func (r *RootCmd) initClientInternal(client *codersdk.Client, allowTokenMissing
|
||||
}
|
||||
}
|
||||
|
||||
func (r *RootCmd) setClient(client *codersdk.Client, serverURL *url.URL) error {
|
||||
func (r *RootCmd) setClient(ctx context.Context, client *codersdk.Client, serverURL *url.URL) error {
|
||||
transport := &headerTransport{
|
||||
transport: http.DefaultTransport,
|
||||
header: http.Header{},
|
||||
}
|
||||
for _, header := range r.header {
|
||||
headers := r.header
|
||||
if r.headerCommand != "" {
|
||||
shell := "sh"
|
||||
caller := "-c"
|
||||
if runtime.GOOS == "windows" {
|
||||
shell = "cmd.exe"
|
||||
caller = "/c"
|
||||
}
|
||||
var outBuf bytes.Buffer
|
||||
// #nosec
|
||||
cmd := exec.CommandContext(ctx, shell, caller, r.headerCommand)
|
||||
cmd.Env = append(os.Environ(), "CODER_URL="+serverURL.String())
|
||||
cmd.Stdout = &outBuf
|
||||
cmd.Stderr = io.Discard
|
||||
err := cmd.Run()
|
||||
if err != nil {
|
||||
return xerrors.Errorf("failed to run %v: %w", cmd.Args, err)
|
||||
}
|
||||
scanner := bufio.NewScanner(&outBuf)
|
||||
for scanner.Scan() {
|
||||
headers = append(headers, scanner.Text())
|
||||
}
|
||||
if err := scanner.Err(); err != nil {
|
||||
return xerrors.Errorf("scan %v: %w", cmd.Args, err)
|
||||
}
|
||||
}
|
||||
for _, header := range headers {
|
||||
parts := strings.SplitN(header, "=", 2)
|
||||
if len(parts) < 2 {
|
||||
return xerrors.Errorf("split header %q had less than two parts", header)
|
||||
@@ -611,9 +647,9 @@ func (r *RootCmd) setClient(client *codersdk.Client, serverURL *url.URL) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (r *RootCmd) createUnauthenticatedClient(serverURL *url.URL) (*codersdk.Client, error) {
|
||||
func (r *RootCmd) createUnauthenticatedClient(ctx context.Context, serverURL *url.URL) (*codersdk.Client, error) {
|
||||
var client codersdk.Client
|
||||
err := r.setClient(&client, serverURL)
|
||||
err := r.setClient(ctx, &client, serverURL)
|
||||
return &client, err
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user