mirror of
https://github.com/kovidgoyal/kitty
synced 2026-07-15 21:14:35 +02:00
Cleanup previous PR
The overrides were not being passed to the askpass kitten. And we dont need to support backward compatibility for secrets with no backend, since this feature has never been released.
This commit is contained in:
@@ -137,6 +137,10 @@ Detailed list of changes
|
||||
0.43.1 [future]
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
- ssh kitten: Allow specifying a password and/or TOTP authentication secret to
|
||||
automate interactive logins in scenarios where public key authentication is
|
||||
not supported (:pull:`9020`)
|
||||
|
||||
- macOS: Fix a bug where the color of a transparent titlebar was off when
|
||||
running in the release build versus the build from source. Also fix using a
|
||||
transparent titlebar causing the background opacity to be doubled.
|
||||
|
||||
@@ -73,7 +73,7 @@ func generateTOTP(secret string, digits, period int64, t time.Time) (string, err
|
||||
off := sum[len(sum)-1] & 0x0f
|
||||
code := (uint32(sum[off])&0x7f)<<24 | (uint32(sum[off+1])&0xff)<<16 | (uint32(sum[off+2])&0xff)<<8 | (uint32(sum[off+3]) & 0xff)
|
||||
mod := uint32(1)
|
||||
for i := int64(0); i < digits; i++ {
|
||||
for range digits {
|
||||
mod *= 10
|
||||
}
|
||||
val := code % mod
|
||||
@@ -96,15 +96,11 @@ func RunSSHAskpass() int {
|
||||
host := os.Getenv("KITTY_SSH_ASKPASS_HOST")
|
||||
user := os.Getenv("KITTY_SSH_ASKPASS_USER")
|
||||
if host != "" {
|
||||
if cfg, bad_lines, err := load_config(host, user, nil); err == nil && cfg != nil {
|
||||
for _, bl := range bad_lines {
|
||||
if bl.Err != nil {
|
||||
// Only fail for our secret backend errors to avoid
|
||||
// unrelated ssh.conf issues breaking askpass.
|
||||
if strings.Contains(bl.Err.Error(), "Unsupported secret backend") {
|
||||
fatal(bl.Err)
|
||||
}
|
||||
}
|
||||
var overrides []string
|
||||
_ = json.Unmarshal([]byte(os.Getenv("KITTY_SSH_ASKPASS_OVERRIDES")), &overrides)
|
||||
if cfg, _, err := load_config(host, user, overrides); err == nil && cfg != nil {
|
||||
if err = resolve_secrets(cfg, false); err != nil {
|
||||
return fatal(err)
|
||||
}
|
||||
// Password autofill
|
||||
if isPasswordPrompt(msg) && cfg.Password != "" {
|
||||
|
||||
@@ -24,10 +24,7 @@ import (
|
||||
|
||||
var _ = fmt.Print
|
||||
|
||||
// parseSecretSetting parses values of the form "backend:secret" for settings where
|
||||
// only the "text" backend is currently supported. If no backend is specified,
|
||||
// the value is treated as a plain text secret for backward compatibility.
|
||||
func parseSecretSetting(settingKey, val string) (string, error) {
|
||||
func resolve_secret(key, val string) (string, error) {
|
||||
v := strings.TrimSpace(val)
|
||||
if v == "" {
|
||||
return "", nil
|
||||
@@ -39,11 +36,25 @@ func parseSecretSetting(settingKey, val string) (string, error) {
|
||||
case "text":
|
||||
return s, nil
|
||||
default:
|
||||
return "", fmt.Errorf("Unsupported secret backend %q for %s. Supported backends: text", b, settingKey)
|
||||
return "", fmt.Errorf("Unsupported secret backend %s for %s. Supported backends: text", b, key)
|
||||
}
|
||||
}
|
||||
// No backend specified; treat as text
|
||||
return v, nil
|
||||
return "", fmt.Errorf("No secret backend specified for: %s", key)
|
||||
}
|
||||
|
||||
func resolve_secrets(c *Config, only_syntax bool) error {
|
||||
_ = only_syntax // this will be useful when using backends that require user interaction
|
||||
if r, err := resolve_secret("password", c.Password); err != nil {
|
||||
return err
|
||||
} else {
|
||||
c.Password = r
|
||||
}
|
||||
if r, err := resolve_secret("totp_secret", c.Totp_secret); err != nil {
|
||||
return err
|
||||
} else {
|
||||
c.Password = r
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
type EnvInstruction struct {
|
||||
@@ -369,7 +380,7 @@ type ConfigSet struct {
|
||||
|
||||
func config_for_hostname(hostname_to_match, username_to_match string, cs *ConfigSet) *Config {
|
||||
matcher := func(q *Config) bool {
|
||||
for _, pat := range strings.Split(q.Hostname, " ") {
|
||||
for pat := range strings.SplitSeq(q.Hostname, " ") {
|
||||
upat := "*"
|
||||
if strings.Contains(pat, "@") {
|
||||
upat, pat, _ = strings.Cut(pat, "@")
|
||||
@@ -401,22 +412,6 @@ func (self *ConfigSet) line_handler(key, val string) error {
|
||||
c = NewConfig()
|
||||
self.all_configs = append(self.all_configs, c)
|
||||
}
|
||||
switch key {
|
||||
case "password":
|
||||
secret, err := parseSecretSetting("password", val)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
c.Password = secret
|
||||
return nil
|
||||
case "totp_secret":
|
||||
secret, err := parseSecretSetting("totp_secret", val)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
c.Totp_secret = secret
|
||||
return nil
|
||||
}
|
||||
return c.Parse(key, val)
|
||||
}
|
||||
|
||||
@@ -438,16 +433,5 @@ func load_config(hostname_to_match string, username_to_match string, overrides [
|
||||
bad_lines = append(bad_lines, override_parser.BadLines()...)
|
||||
final_conf.Hostname = h
|
||||
}
|
||||
// Normalize and validate secrets post-overrides as overrides bypass line_handler
|
||||
if s, err := parseSecretSetting("password", final_conf.Password); err != nil {
|
||||
bad_lines = append(bad_lines, config.ConfigLine{Src_file: "<overrides>", Line: "password " + final_conf.Password, Line_number: 0, Err: err})
|
||||
} else {
|
||||
final_conf.Password = s
|
||||
}
|
||||
if s, err := parseSecretSetting("totp_secret", final_conf.Totp_secret); err != nil {
|
||||
bad_lines = append(bad_lines, config.ConfigLine{Src_file: "<overrides>", Line: "totp_secret " + final_conf.Totp_secret, Line_number: 0, Err: err})
|
||||
} else {
|
||||
final_conf.Totp_secret = s
|
||||
}
|
||||
return final_conf, bad_lines, nil
|
||||
}
|
||||
|
||||
@@ -145,7 +145,7 @@ func connection_sharing_args(kitty_pid int) ([]string, error) {
|
||||
}, nil
|
||||
}
|
||||
|
||||
func set_askpass() (need_to_request_data bool) {
|
||||
func set_askpass(hostname_for_match, uname string, overrides []string) (need_to_request_data bool) {
|
||||
need_to_request_data = true
|
||||
sentinel := filepath.Join(utils.CacheDir(), "openssh-is-new-enough-for-askpass")
|
||||
_, err := os.Stat(sentinel)
|
||||
@@ -160,6 +160,11 @@ func set_askpass() (need_to_request_data bool) {
|
||||
if err == nil {
|
||||
os.Setenv("SSH_ASKPASS", exe)
|
||||
os.Setenv("KITTY_KITTEN_RUN_MODULE", "ssh_askpass")
|
||||
// Provide data to askpass so it can lookup auth settings in ssh.conf
|
||||
os.Setenv("KITTY_SSH_ASKPASS_HOST", hostname_for_match)
|
||||
os.Setenv("KITTY_SSH_ASKPASS_USER", uname)
|
||||
ov, _ := json.Marshal(overrides)
|
||||
os.Setenv("KITTY_SSH_ASKPASS_OVERRIDES", string(ov))
|
||||
if !need_to_request_data {
|
||||
os.Setenv("SSH_ASKPASS_REQUIRE", "force")
|
||||
}
|
||||
@@ -621,6 +626,11 @@ func run_ssh(ssh_args, server_args, found_extra_args []string) (rc int, err erro
|
||||
if err != nil {
|
||||
return 1, err
|
||||
}
|
||||
// check the secrets syntax here as askpass has no good way to report errors to
|
||||
// the user
|
||||
if err = resolve_secrets(host_opts, true); err != nil {
|
||||
return 1, err
|
||||
}
|
||||
if len(bad_lines) > 0 {
|
||||
for _, x := range bad_lines {
|
||||
fmt.Fprintf(os.Stderr, "Ignoring bad config line: %s:%d with error: %s", filepath.Base(x.Src_file), x.Line_number, x.Err)
|
||||
@@ -646,14 +656,11 @@ func run_ssh(ssh_args, server_args, found_extra_args []string) (rc int, err erro
|
||||
}
|
||||
cmd = slices.Insert(cmd, insertion_point, control_master_args...)
|
||||
}
|
||||
use_kitty_askpass := host_opts.Askpass == Askpass_native || (host_opts.Askpass == Askpass_unless_set && os.Getenv("SSH_ASKPASS") == "")
|
||||
need_to_request_data := true
|
||||
if use_kitty_askpass {
|
||||
need_to_request_data = set_askpass()
|
||||
// Provide host/user to askpass so it can lookup auth settings in ssh.conf
|
||||
os.Setenv("KITTY_SSH_ASKPASS_HOST", hostname_for_match)
|
||||
os.Setenv("KITTY_SSH_ASKPASS_USER", uname)
|
||||
}
|
||||
use_kitty_askpass := host_opts.Askpass == Askpass_native || (host_opts.Askpass == Askpass_unless_set && os.Getenv("SSH_ASKPASS") == "")
|
||||
need_to_request_data := true
|
||||
if use_kitty_askpass {
|
||||
need_to_request_data = set_askpass(hostname_for_match, uname, overrides)
|
||||
}
|
||||
master_is_functional := func() bool {
|
||||
if master_checked {
|
||||
return master_is_alive
|
||||
|
||||
@@ -223,7 +223,7 @@ egr() # }}}
|
||||
agr('askpass', 'Askpass automation') # {{{
|
||||
|
||||
opt('password', '', long_text='''
|
||||
Specify a secret to use when SSH prompts for a password. The value format is
|
||||
Specify a password to use when SSH prompts for a password. The value format is
|
||||
"backend:secret". Currently, only the "text" backend is supported, which stores
|
||||
the secret in plain text in the config file. For example:
|
||||
|
||||
|
||||
Reference in New Issue
Block a user