package ssh import ( "fmt" "os" "os/user" "path" "path/filepath" "strconv" ) const ( BackendName = "ssh" HostKeyPolicyStrict HostKeyPolicy = "strict" HostKeyPolicyAcceptNew HostKeyPolicy = "accept-new" HostKeyPolicyOff HostKeyPolicy = "off" ) type HostKeyPolicy string type Options struct { Host string User string Port int Root string KeyFile string KnownHosts string HostKeyPolicy HostKeyPolicy ReadOnlyKnownHosts bool } func (o Options) normalized() (Options, error) { if o.Host == "" { return Options{}, fmt.Errorf("host is required") } if o.User == "" { current, err := user.Current() if err != nil || current.Username == "" { return Options{}, fmt.Errorf("user is required when current OS user cannot be determined") } o.User = current.Username } if o.Port == 0 { o.Port = 22 } if o.Port < 1 || o.Port > 65535 { return Options{}, fmt.Errorf("port must be between 1 and 65535") } if o.Root == "" { return Options{}, fmt.Errorf("path is required") } o.Root = path.Clean(o.Root) if o.HostKeyPolicy == "" { o.HostKeyPolicy = HostKeyPolicyAcceptNew } switch o.HostKeyPolicy { case HostKeyPolicyStrict, HostKeyPolicyAcceptNew, HostKeyPolicyOff: default: return Options{}, fmt.Errorf("host_key_policy must be strict, accept-new, or off") } if o.KnownHosts == "" && o.HostKeyPolicy != HostKeyPolicyOff { o.KnownHosts = defaultKnownHostsPath() } return o, nil } func (o Options) address() string { return o.Host + ":" + strconv.Itoa(o.Port) } func defaultKnownHostsPath() string { home, err := os.UserHomeDir() if err != nil || home == "" { return "" } return filepath.Join(home, ".ssh", "known_hosts") }