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

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

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
}

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

(0)
上一篇 2022年3月21日
下一篇 2022年3月21日

相关推荐