欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页  >  IT编程

golang 通过ssh代理连接mysql的操作

程序员文章站 2022-03-24 09:17:01
我就废话不多说了,大家还是直接看代码吧~package mainimport ("bytes""context""database/sql""errors""fmt""github.com/go-sq...

我就废话不多说了,大家还是直接看代码吧~

package main
import (
	"bytes"
	"context"
	"database/sql"
	"errors"
	"fmt"
	"github.com/go-sql-driver/mysql"
	"golang.org/x/crypto/ssh"
	"io"
	"io/ioutil"
	"net"
	"os"
)
type viasshdialer struct {
	client *ssh.client
	_ *context.context
}
 
func (self *viasshdialer) dial(context context.context,addr string) (net.conn, error) {
	return self.client.dial("tcp", addr)
}
type remotescripttype byte
type remoteshelltype byte
 
const (
	cmdline remotescripttype = iota
	rawscript
	scriptfile 
	interactiveshell remoteshelltype = iota
	noninteractiveshell
)
 
type client struct {
	client *ssh.client
}
func main() {
	client, err := dialwithpasswd("ip:port", "user", "password")
	if err != nil {
		panic(err)
	}
	out, err := client.cmd("ls -l").output()
	if err != nil {
		panic(err)
	}
	fmt.println(string(out))
	// now we register the viasshdialer with the ssh connection as a parameter
	mysql.registerdialcontext("mysql+tcp", (&viasshdialer{client.client,nil}).dial)
	//mysql.registerdial("mysql+tcp", (&viasshdialer{client.client}).dial)
	if db, err := sql.open("mysql", fmt.sprintf("%s:%s@mysql+tcp(%s)/%s","aiqitest", "uf6amk146d2aoemi7", "139.196.174.234:3306", "aiqitest"));
	err == nil {
		fmt.printf("successfully connected to the db\n")
		if rows, err := db.query("select id, name from table order by id"); err == nil {
			for rows.next() {
				var id int64
				var name string
				rows.scan(&id, &name)
				fmt.printf("id: %d name: %s\n", id, name)
			}
			rows.close()
		} else {
			fmt.printf("failure: %s", err.error())
		}
 
		db.close()
}
	}
 
// dialwithpasswd starts a client connection to the given ssh server with passwd authmethod.
func dialwithpasswd(addr, user, passwd string) (*client, error) {
	config := &ssh.clientconfig{
		user: user,
		auth: []ssh.authmethod{
			ssh.password(passwd),
		},
		hostkeycallback: ssh.hostkeycallback(func(hostname string, remote net.addr, key ssh.publickey) error { return nil }),
	}
 
	return dial("tcp", addr, config)
}
 
// dialwithkey starts a client connection to the given ssh server with key authmethod.
func dialwithkey(addr, user, keyfile string) (*client, error) {
	key, err := ioutil.readfile(keyfile)
	if err != nil {
		return nil, err
	}
 
	signer, err := ssh.parseprivatekey(key)
	if err != nil {
		return nil, err
	}
 
	config := &ssh.clientconfig{
		user: user,
		auth: []ssh.authmethod{
			ssh.publickeys(signer),
		},
		hostkeycallback: ssh.hostkeycallback(func(hostname string, remote net.addr, key ssh.publickey) error { return nil }),
	}
 
	return dial("tcp", addr, config)
}
 
// dialwithkeywithpassphrase same as dialwithkey but with a passphrase to decrypt the private key
func dialwithkeywithpassphrase(addr, user, keyfile string, passphrase string) (*client, error) {
	key, err := ioutil.readfile(keyfile)
	if err != nil {
		return nil, err
	}
 
	signer, err := ssh.parseprivatekeywithpassphrase(key, []byte(passphrase))
	if err != nil {
		return nil, err
	}
 
	config := &ssh.clientconfig{
		user: user,
		auth: []ssh.authmethod{
			ssh.publickeys(signer),
		},
		hostkeycallback: ssh.hostkeycallback(func(hostname string, remote net.addr, key ssh.publickey) error { return nil }),
	} 
	return dial("tcp", addr, config)
}
 
// dial starts a client connection to the given ssh server.
// this is wrap the ssh.dial
func dial(network, addr string, config *ssh.clientconfig) (*client, error) {
	client, err := ssh.dial(network, addr, config)
	if err != nil {
		return nil, err
	}
	return &client{
		client: client,
	}, nil
}
 
func (c *client) close() error {
	return c.client.close()
}
 
// cmd create a command on client
func (c *client) cmd(cmd string) *remotescript {
	return &remotescript{
		_type: cmdline,
		client: c.client,
		script: bytes.newbufferstring(cmd + "\n"),
	}
}
 
// script
func (c *client) script(script string) *remotescript {
	return &remotescript{
		_type: rawscript,
		client: c.client,
		script: bytes.newbufferstring(script + "\n"),
	}
}
 
// scriptfile
func (c *client) scriptfile(fname string) *remotescript {
	return &remotescript{
		_type:   scriptfile,
		client:   c.client,
		scriptfile: fname,
	}
}
 
type remotescript struct {
	client   *ssh.client
	_type   remotescripttype
	script   *bytes.buffer
	scriptfile string
	err    error
 
	stdout io.writer
	stderr io.writer
}
 
// run
func (rs *remotescript) run() error {
	if rs.err != nil {
		fmt.println(rs.err)
		return rs.err
	}
 
	if rs._type == cmdline {
		return rs.runcmds()
	} else if rs._type == rawscript {
		return rs.runscript()
	} else if rs._type == scriptfile {
		return rs.runscriptfile()
	} else {
		return errors.new("not supported remotescript type")
	}
}
 
func (rs *remotescript) output() ([]byte, error) {
	if rs.stdout != nil {
		return nil, errors.new("stdout already set")
	}
	var out bytes.buffer
	rs.stdout = &out
	err := rs.run()
	return out.bytes(), err
}
 
func (rs *remotescript) smartoutput() ([]byte, error) {
	if rs.stdout != nil {
		return nil, errors.new("stdout already set")
	}
	if rs.stderr != nil {
		return nil, errors.new("stderr already set")
	}
 
	var (
		stdout bytes.buffer
		stderr bytes.buffer
	)
	rs.stdout = &stdout
	rs.stderr = &stderr
	err := rs.run()
	if err != nil {
		return stderr.bytes(), err
	}
	return stdout.bytes(), err
}
 
func (rs *remotescript) cmd(cmd string) *remotescript {
	_, err := rs.script.writestring(cmd + "\n")
	if err != nil {
		rs.err = err
	}
	return rs
}
 
func (rs *remotescript) setstdio(stdout, stderr io.writer) *remotescript {
	rs.stdout = stdout
	rs.stderr = stderr
	return rs
}
 
func (rs *remotescript) runcmd(cmd string) error {
	session, err := rs.client.newsession()
	if err != nil {
		return err
	}
	defer session.close()
 
	session.stdout = rs.stdout
	session.stderr = rs.stderr
 
	if err := session.run(cmd); err != nil {
		return err
	}
	return nil
}
 
func (rs *remotescript) runcmds() error {
	for {
		statment, err := rs.script.readstring('\n')
		if err == io.eof {
			break
		}
		if err != nil {
			return err
		}
 
		if err := rs.runcmd(statment); err != nil {
			return err
		}
	}
 
	return nil
}
 
func (rs *remotescript) runscript() error {
	session, err := rs.client.newsession()
	if err != nil {
		return err
	}
 
	session.stdin = rs.script
	session.stdout = rs.stdout
	session.stderr = rs.stderr 
	if err := session.shell(); err != nil {
		return err
	}
	if err := session.wait(); err != nil {
		return err
	}
 
	return nil
}
 
func (rs *remotescript) runscriptfile() error {
	var buffer bytes.buffer
	file, err := os.open(rs.scriptfile)
	if err != nil {
		return err
	}
	_, err = io.copy(&buffer, file)
	if err != nil {
		return err
	}
 
	rs.script = &buffer
	return rs.runscript()
}
 
type remoteshell struct {
	client     *ssh.client
	requestpty   bool
	terminalconfig *terminalconfig
 
	stdin io.reader
	stdout io.writer
	stderr io.writer
}
 
type terminalconfig struct {
	term  string
	hight int
	weight int
	modes ssh.terminalmodes
}
 
// terminal create a interactive shell on client.
func (c *client) terminal(config *terminalconfig) *remoteshell {
	return &remoteshell{
		client:     c.client,
		terminalconfig: config,
		requestpty:   true,
	}
}
 
// shell create a noninteractive shell on client.
func (c *client) shell() *remoteshell {
	return &remoteshell{
		client:   c.client,
		requestpty: false,
	}
}
 
func (rs *remoteshell) setstdio(stdin io.reader, stdout, stderr io.writer) *remoteshell {
	rs.stdin = stdin
	rs.stdout = stdout
	rs.stderr = stderr
	return rs
}
 
// start start a remote shell on client
func (rs *remoteshell) start() error {
	session, err := rs.client.newsession()
	if err != nil {
		return err
	}
	defer session.close()
 
	if rs.stdin == nil {
		session.stdin = os.stdin
	} else {
		session.stdin = rs.stdin
	}
	if rs.stdout == nil {
		session.stdout = os.stdout
	} else {
		session.stdout = rs.stdout
	}
	if rs.stderr == nil {
		session.stderr = os.stderr
	} else {
		session.stderr = rs.stderr
	}
 
	if rs.requestpty {
		tc := rs.terminalconfig
		if tc == nil {
			tc = &terminalconfig{
				term:  "xterm",
				hight: 40,
				weight: 80,
			}
		}
		if err := session.requestpty(tc.term, tc.hight, tc.weight, tc.modes); err != nil {
			return err
		}
	}
 
	if err := session.shell(); err != nil {
		return err
	}
 
	if err := session.wait(); err != nil {
		return err
	} 
	return nil
}

补充:用golang写socks5代理服务器2-ssh远程代理

上次用golang来实现本地socks5代理,然而使用代理当然是为了和谐上网,所以这次来介绍用ssh来实现远程代理,用到官方ssh包

golang.org/x/crypto/ssh

用golang连接ssh并不难

读取密钥,设置配置,连接服务器就ok了(不建议用用户名+密码方式连接ssh)

 b, err := ioutil.readfile("/home/myml/.ssh/id_rsa")
 if err != nil {
 log.println(err)
 return
 }
 pkey, err := ssh.parseprivatekey(b)
 if err != nil {
 log.println(err)
 return
 }
 config := ssh.clientconfig{
 user: "username",
 auth: []ssh.authmethod{
  ssh.publickeys(pkey),
 },
 }
 client, err = ssh.dial("tcp", "host:22", &config)
 if err != nil {
 log.println(err)
 return
 }
 log.println("连接服务器成功")
 defer client.close()

这样你就得到了一个client,它有个dial()函数用来创建socket连接,这个是在服务器上创建的,也就可以突破网络限制了,加上上次的sock5代理,把net.dial改为client.dial,就能让服务器来代理访问了

 server, err := client.dial("tcp", addr)
 if err != nil {
 log.println(err)
 return
 }
 conn.write([]byte{0x05, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00})
 go io.copy(server, conn)
 io.copy(conn, server)

下面是能成功运行并进行远程代理的代码(在chrome和proxychains测试),ssh服务器和配置信息要修改为自己的

// socks5proxyproxy project main.go
package main 
import (
 "bytes"
 "encoding/binary"
 "fmt"
 "io"
 "io/ioutil"
 "log"
 "net" 
 "golang.org/x/crypto/ssh"
)
 
func socks5proxy(conn net.conn) {
 defer conn.close()
 
 var b [1024]byte 
 n, err := conn.read(b[:])
 if err != nil {
 log.println(err)
 return
 }
 log.printf("% x", b[:n]) 
 conn.write([]byte{0x05, 0x00})
 
 n, err = conn.read(b[:])
 if err != nil {
 log.println(err)
 return
 }
 log.printf("% x", b[:n])
 
 var addr string
 switch b[3] {
 case 0x01:
 sip := sockip{}
 if err := binary.read(bytes.newreader(b[4:n]), binary.bigendian, &sip); err != nil {
  log.println("请求解析错误")
  return
 }
 addr = sip.toaddr()
 case 0x03:
 host := string(b[5 : n-2])
 var port uint16
 err = binary.read(bytes.newreader(b[n-2:n]), binary.bigendian, &port)
 if err != nil {
  log.println(err)
  return
 }
 addr = fmt.sprintf("%s:%d", host, port)
 }
 
 server, err := client.dial("tcp", addr)
 if err != nil {
 log.println(err)
 return
 }
 conn.write([]byte{0x05, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00})
 go io.copy(server, conn)
 io.copy(conn, server)
}
 
type sockip struct {
 a, b, c, d byte
 port    uint16
}
 
func (ip sockip) toaddr() string {
 return fmt.sprintf("%d.%d.%d.%d:%d", ip.a, ip.b, ip.c, ip.d, ip.port)
}
 
func socks5proxystart() {
 log.setflags(log.ltime | log.lshortfile)
 
 server, err := net.listen("tcp", ":8080")
 if err != nil {
 log.panic(err)
 }
 defer server.close()
 log.println("开始接受连接")
 for {
 client, err := server.accept()
 if err != nil {
  log.println(err)
  return
 }
 log.println("一个新连接")
 go socks5proxy(client)
 }
}
 
var client *ssh.client 
func main() {
 b, err := ioutil.readfile("/home/myml/.ssh/id_rsa")
 if err != nil {
 log.println(err)
 return
 }
 pkey, err := ssh.parseprivatekey(b)
 if err != nil {
 log.println(err)
 return
 }
 config := ssh.clientconfig{
 user: "user",
 auth: []ssh.authmethod{
  ssh.publickeys(pkey),
 },
 }
 client, err = ssh.dial("tcp", "host:22", &config)
 if err != nil {
 log.println(err)
 return
 }
 log.println("连接服务器成功")
 defer client.close()
 client.dial()
 socks5proxystart()
 return
}
 

以上为个人经验,希望能给大家一个参考,也希望大家多多支持。如有错误或未考虑完全的地方,望不吝赐教。

相关标签: golang ssh mysql