ssh/auth.go

189 lines
5.0 KiB
Go

package ssh
import (
"errors"
"fmt"
"io/ioutil"
"net"
"os"
"strings"
"syscall"
"golang.org/x/crypto/ssh"
"golang.org/x/crypto/ssh/agent"
"golang.org/x/crypto/ssh/terminal"
)
//HasAgent reports whether the SSH agent is available
func HasAgent() bool {
authsock, ok := os.LookupEnv("SSH_AUTH_SOCK")
if !ok {
return false
}
if dirent, err := os.Stat(authsock); err != nil {
if os.IsNotExist(err) {
return false
}
if dirent.Mode()&os.ModeSocket == 0 {
return false
}
}
return true
}
// An implementation of ssh.KeyboardInteractiveChallenge that simply sends
// back the password for all questions. The questions are logged.
func passwordKeyboardInteractive(password string) ssh.KeyboardInteractiveChallenge {
return func(user, instruction string, questions []string, echos []bool) ([]string, error) {
// log.Printf("Keyboard interactive challenge: ")
// log.Printf("-- User: %s", user)
// log.Printf("-- Instructions: %s", instruction)
// for i, question := range questions {
// log.Printf("-- Question %d: %s", i+1, question)
// }
// Just send the password back for all questions
answers := make([]string, len(questions))
for i := range answers {
answers[i] = password
}
return answers, nil
}
}
// WithKeyboardPassword Generate a password-auth'd ssh ClientConfig
func WithKeyboardPassword(password string) (ssh.AuthMethod, error) {
return ssh.KeyboardInteractive(passwordKeyboardInteractive(password)), nil
}
// WithPassword Generate a password-auth'd ssh ClientConfig
func WithPassword(password string) (ssh.AuthMethod, error) {
return ssh.Password(password), nil
}
// WithAgent use already authed user
func WithAgent() (ssh.AuthMethod, error) {
sock := os.Getenv("SSH_AUTH_SOCK")
if sock == "" {
// fmt.Println(errors.New("Agent Disabled"))
return nil, errors.New("Agent Disabled")
}
socks, err := net.Dial("unix", sock)
if err != nil {
fmt.Println(err)
return nil, err
}
// 1. 返回Signers函数的结果
agent := agent.NewClient(socks)
signers, err := agent.Signers()
return ssh.PublicKeys(signers...), nil
// 2. 返回Signers函数
// getSigners := agent.NewClient(socks).Signers
// return ssh.PublicKeysCallback(getSigners), nil
// 3.简写方式
// if sshAgent, err := net.Dial("unix", os.Getenv("SSH_AUTH_SOCK")); err == nil {
// return ssh.PublicKeysCallback(agent.NewClient(sshAgent).Signers)
// }
// return nil
}
// WithPrivateKeys 设置多个 ~/.ssh/id_rsa ,如果加密用passphrase尝试
func WithPrivateKeys(keyFiles []string, passphrase string) (ssh.AuthMethod, error) {
var signers []ssh.Signer
for _, key := range keyFiles {
pemBytes, err := ioutil.ReadFile(key)
if err != nil {
println(err.Error())
// return
}
signer, err := ssh.ParsePrivateKey([]byte(pemBytes))
if err != nil {
if strings.Contains(err.Error(), "cannot decode encrypted private keys") {
if signer, err = ssh.ParsePrivateKeyWithPassphrase(pemBytes, []byte(passphrase)); err != nil {
continue
}
}
// println(err.Error())
}
signers = append(signers, signer)
}
if signers == nil {
return nil, errors.New("WithPrivateKeys: no keyfiles input")
}
return ssh.PublicKeys(signers...), nil
}
// WithPrivateKey 自动监测是否带有密码
func WithPrivateKey(keyfile string, passphrase string) (ssh.AuthMethod, error) {
pemBytes, err := ioutil.ReadFile(keyfile)
if err != nil {
println(err.Error())
return nil, err
}
var signer ssh.Signer
signer, err = ssh.ParsePrivateKey(pemBytes)
if err != nil {
if strings.Contains(err.Error(), "cannot decode encrypted private keys") {
signer, err = ssh.ParsePrivateKeyWithPassphrase(pemBytes, []byte(passphrase))
if err == nil {
return ssh.PublicKeys(signer), nil
}
}
return nil, err
}
return ssh.PublicKeys(signer), nil
}
// WithPrivateKeyString 直接通过字符串
func WithPrivateKeyString(key string, password string) (ssh.AuthMethod, error) {
var signer ssh.Signer
var err error
if password == "" {
signer, err = ssh.ParsePrivateKey([]byte(key))
} else {
signer, err = ssh.ParsePrivateKeyWithPassphrase([]byte(key), []byte(password))
}
if err != nil {
println(err.Error())
return nil, err
}
return ssh.PublicKeys(signer), nil
}
// WithPrivateKeyTerminal 通过终端读取带密码的 PublicKey
func WithPrivateKeyTerminal(keyfile string) (ssh.AuthMethod, error) {
pemBytes, err := ioutil.ReadFile(keyfile)
if err != nil {
println(err.Error())
return nil, err
}
var signer ssh.Signer
signer, err = ssh.ParsePrivateKey(pemBytes)
if err != nil {
if strings.Contains(err.Error(), "cannot decode encrypted private keys") {
fmt.Fprintf(os.Stderr, "This SSH key is encrypted. Please enter passphrase for key '%s':", keyfile)
passphrase, err := terminal.ReadPassword(int(syscall.Stdin))
if err != nil {
// println(err.Error())
return nil, err
}
fmt.Fprintln(os.Stderr)
if signer, err = ssh.ParsePrivateKeyWithPassphrase(pemBytes, []byte(passphrase)); err == nil {
return ssh.PublicKeys(signer), nil
}
}
return nil, err
}
return ssh.PublicKeys(signer), nil
}