diff --git a/cmd_credential.go b/cmd_credential.go index 5576e2f..7158be2 100644 --- a/cmd_credential.go +++ b/cmd_credential.go @@ -1,14 +1,17 @@ package main import ( + "cmp" "crypto/sha256" "encoding/json" "fmt" "io" "net/http" "os" + "os/signal" "path/filepath" "strings" + "syscall" "time" "golang.org/x/term" @@ -196,7 +199,7 @@ func credReadCache(serverURL string, logf func(string, ...interface{})) (*ExecCr return &ec, true } -func credWriteCache(serverURL string, ec *ExecCredential, logf func(string, ...interface{})) { +func credWriteCache(serverURL string, ec *ExecCredential, logf func(string, ...any)) { dir := credCacheDir() if err := os.MkdirAll(dir, 0700); err != nil { logf("cache: failed to create dir: %v", err) @@ -218,10 +221,7 @@ func credWriteCache(serverURL string, ec *ExecCredential, logf func(string, ...i // ── Kerberos helpers ─────────────────────────────────────────────────────────── func krb5ConfigPath() string { - if v := os.Getenv("KRB5_CONFIG"); v != "" { - return v - } - return "/etc/krb5.conf" + return cmp.Or(os.Getenv("KRB5_CONFIG"), "/etc/krb5.conf") } // ccachePath returns the path to the active Kerberos credential cache. @@ -238,14 +238,31 @@ func ccachePath() string { // ── Password prompt ──────────────────────────────────────────────────────────── func credPromptPassword(username string) (string, error) { - if !term.IsTerminal(int(os.Stdin.Fd())) { + terminal, err := os.OpenFile("/dev/tty", os.O_RDWR, 0) + if err != nil { return "", fmt.Errorf( - "stdin is not a terminal and $WARD_PASSWORD is not set\n" + + "cannot open terminal and $WARD_PASSWORD is not set\n" + "hint: run 'kinit' for Kerberos auth, or set $WARD_PASSWORD for non-interactive use") } - fmt.Fprintf(os.Stderr, "Password for %s: ", username) - pw, err := term.ReadPassword(int(os.Stdin.Fd())) - fmt.Fprintln(os.Stderr) // newline after the hidden input + + oldState, err := term.MakeRaw(int(terminal.Fd())) + if err != nil { + return "", fmt.Errorf("setting terminal raw mode: %w", err) + } + + sigCh := make(chan os.Signal, 1) + signal.Notify(sigCh, os.Interrupt, syscall.SIGTERM) + go func() { + <-sigCh + term.Restore(int(terminal.Fd()), oldState) + os.Exit(1) + }() + + fmt.Fprintf(terminal, "Password for %s: ", username) + pw, err := term.ReadPassword(int(terminal.Fd())) + fmt.Fprintf(terminal, "\r\n") // newline after the hidden input + signal.Stop(sigCh) + term.Restore(int(terminal.Fd()), oldState) if err != nil { return "", fmt.Errorf("reading password: %w", err) } diff --git a/main.go b/main.go index cdb723d..3b877af 100644 --- a/main.go +++ b/main.go @@ -253,6 +253,17 @@ Debug output goes to stderr (kubectl surfaces this to the terminal): return fmt.Errorf("--server is required") } + // prepend https if no scheme is given, for user convenience + if !strings.Contains(server, "://") { + server = "https://" + server + } + + // append port 8443 if no port is given + parts := strings.Split(server, ":") + if len(parts) == 2 { + server += ":8443" + } + logf := func(format string, a ...interface{}) { if debugFlag { fmt.Fprintf(os.Stderr, "[ward] "+format+"\n", a...)