Improve terminal handling and make server arg more helpful

This commit is contained in:
2026-03-26 14:03:09 +01:00
parent cd56349517
commit 24f1fd0477
2 changed files with 38 additions and 10 deletions

View File

@@ -1,14 +1,17 @@
package main package main
import ( import (
"cmp"
"crypto/sha256" "crypto/sha256"
"encoding/json" "encoding/json"
"fmt" "fmt"
"io" "io"
"net/http" "net/http"
"os" "os"
"os/signal"
"path/filepath" "path/filepath"
"strings" "strings"
"syscall"
"time" "time"
"golang.org/x/term" "golang.org/x/term"
@@ -196,7 +199,7 @@ func credReadCache(serverURL string, logf func(string, ...interface{})) (*ExecCr
return &ec, true 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() dir := credCacheDir()
if err := os.MkdirAll(dir, 0700); err != nil { if err := os.MkdirAll(dir, 0700); err != nil {
logf("cache: failed to create dir: %v", err) logf("cache: failed to create dir: %v", err)
@@ -218,10 +221,7 @@ func credWriteCache(serverURL string, ec *ExecCredential, logf func(string, ...i
// ── Kerberos helpers ─────────────────────────────────────────────────────────── // ── Kerberos helpers ───────────────────────────────────────────────────────────
func krb5ConfigPath() string { func krb5ConfigPath() string {
if v := os.Getenv("KRB5_CONFIG"); v != "" { return cmp.Or(os.Getenv("KRB5_CONFIG"), "/etc/krb5.conf")
return v
}
return "/etc/krb5.conf"
} }
// ccachePath returns the path to the active Kerberos credential cache. // ccachePath returns the path to the active Kerberos credential cache.
@@ -238,14 +238,31 @@ func ccachePath() string {
// ── Password prompt ──────────────────────────────────────────────────────────── // ── Password prompt ────────────────────────────────────────────────────────────
func credPromptPassword(username string) (string, error) { 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( 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") "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())) oldState, err := term.MakeRaw(int(terminal.Fd()))
fmt.Fprintln(os.Stderr) // newline after the hidden input 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 { if err != nil {
return "", fmt.Errorf("reading password: %w", err) return "", fmt.Errorf("reading password: %w", err)
} }

11
main.go
View File

@@ -253,6 +253,17 @@ Debug output goes to stderr (kubectl surfaces this to the terminal):
return fmt.Errorf("--server is required") 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{}) { logf := func(format string, a ...interface{}) {
if debugFlag { if debugFlag {
fmt.Fprintf(os.Stderr, "[ward] "+format+"\n", a...) fmt.Fprintf(os.Stderr, "[ward] "+format+"\n", a...)