如何让我的GCloud函数打开一个新的SSH连接来访问SFTP服务器?

huangapple go评论98阅读模式
英文:

How can I make my GCloud Function open a new SSH connection to consume a SFTP server?

问题

我的设置需要一个Google函数来执行一些操作并将结果上传到SFTP服务器。我目前使用基本的sftpcrypto/ssh包来实现这一点。在本地调试后,我成功获取了服务器的公钥。

当部署到GCloud时,当然什么都不起作用。

这是我函数中处理连接的代码:

func Connect(host string, port string, user string, password string) (*ssh.Client, error) {
	hostKey := getHostKey(host)

	var auths []ssh.AuthMethod

	// 如果提供了密码,则使用密码身份验证
	if password != "" {
		auths = append(auths, ssh.Password(password))
	}

	config := &ssh.ClientConfig{
		User:            user,
		HostKeyCallback: ssh.FixedHostKey(hostKey),
		Auth:            auths,
	}

	cipherOrder := config.Ciphers
	config.Ciphers = append(cipherOrder, "aes128-cbc", "3des-cbc")

	sshConn, err := ssh.Dial("tcp", host+":"+port, config)
	if err != nil {
		return nil, err
	}

	return sshConn, nil
}

func getHostKey(host string) ssh.PublicKey {
	file, err := os.Open("/root/.ssh/known_hosts")
	if err != nil {
		fmt.Fprintf(os.Stderr, "无法读取known_hosts文件:%v\n", err)
		os.Exit(1)
	}
	defer file.Close()

	scanner := bufio.NewScanner(file)
	var hostKey ssh.PublicKey
	for scanner.Scan() {
		fields := strings.Split(scanner.Text(), " ")
		if len(fields) != 3 {
			continue
		}
		if strings.Contains(fields[0], host) {
			var err error
			hostKey, _, _, _, err = ssh.ParseAuthorizedKey(scanner.Bytes())
			if err != nil {
				fmt.Fprintf(os.Stderr, "解析%q时出错:%v\n", fields[2], err)
				os.Exit(1)
			}
			break
		}
	}

	if hostKey == nil {
		fmt.Fprintf(os.Stderr, "找不到%s的主机密钥", host)
		os.Exit(1)
	}

	return hostKey
}

known_hosts文件不存在。我没有服务器的公钥,但是使用Filezilla我可以正常连接到它。

我不得不指定这些密码,因为一个简单的ssh hostname命令会返回无法协商...错误。

有其他的方法可以做到这一点吗?我在考虑上传自己的known_hosts文件,但这听起来不是一个好的解决方案。

英文:

My setup requires a Google Function to do some stuff and upload the result to a SFTP server. I'm currently using the basic sftp and crypto/ssh packages to achieve this. Locally, after some debugging, I was able to retrieve the server's pubkey.

When deploying to GCloud nothing works, of course.

This is what handles the connection on my function

func Connect(host string, port string, user string, password string) (*ssh.Client, error) {
	hostKey := getHostKey(host)

	var auths []ssh.AuthMethod

	// Use password authentication if provided
	if password != "" {
		auths = append(auths, ssh.Password(password))
	}

	config := &ssh.ClientConfig{
		User:            user,
		HostKeyCallback: ssh.FixedHostKey(hostKey),
		Auth:            auths,
	}

	cipherOrder := config.Ciphers
	config.Ciphers = append(cipherOrder, "aes128-cbc", "3des-cbc")

	sshConn, err := ssh.Dial("tcp", host+":"+port, config)
	if err != nil {
		return nil, err
	}

	return sshConn, nil
}

func getHostKey(host string) ssh.PublicKey {
	file, err := os.Open("/root/.ssh/known_hosts")
	if err != nil {
		fmt.Fprintf(os.Stderr, "Unable to read known_hosts file: %v\n", err)
		os.Exit(1)
	}
	defer file.Close()

	scanner := bufio.NewScanner(file)
	var hostKey ssh.PublicKey
	for scanner.Scan() {
		fields := strings.Split(scanner.Text(), " ")
		if len(fields) != 3 {
			continue
		}
		if strings.Contains(fields[0], host) {
			var err error
			hostKey, _, _, _, err = ssh.ParseAuthorizedKey(scanner.Bytes())
			if err != nil {
				fmt.Fprintf(os.Stderr, "Error parsing %q: %v\n", fields[2], err)
				os.Exit(1)
			}
			break
		}
	}

	if hostKey == nil {
		fmt.Fprintf(os.Stderr, "No hostkey found for %s", host)
		os.Exit(1)
	}

	return hostKey
}

The known_hosts file doesn't exist. I don't have the pubkey of the server, but with Filezilla I can connect to it just fine.

I had to specify those cyphers because a barebone ssh hostname would return Unable to negotiate... error

Is there any other way to do this? I'm thinking about uploading my own known_hosts file but it doesn't sound like a great solution.

答案1

得分: 0

我可能过度工程化了。

像这样设置ssh.ClientConfig解决了问题:

config := &ssh.ClientConfig{
    User:            user,
    Auth:            auths,
    HostKeyCallback: ssh.InsecureIgnoreHostKey(),
}

无论如何,我还找到了一个更好的包来轻松处理SSH连接,simplessh

conn, _ := simplessh.ConnectWithPassword(host, user, pass)

client, _ := sftp.NewClient(conn.SSHClient)
英文:

I was probably over engineering it.

Setting the ssh.ClientConfig like this solved the problem:

config := &ssh.ClientConfig{
		User:            user,
		Auth:            auths,
		HostKeyCallback: ssh.InsecureIgnoreHostKey(),
}

Anyway I also found a nicer package to easily handle SSH connection, simplessh.

conn, _ := simplessh.ConnectWithPassword(host, user, pass)

client, _ := sftp.NewClient(conn.SSHClient)

huangapple
  • 本文由 发表于 2021年5月26日 16:07:38
  • 转载请务必保留本文链接:https://go.coder-hub.com/67700931.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定