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