-
Notifications
You must be signed in to change notification settings - Fork 5
/
example_test.go
169 lines (148 loc) · 5.89 KB
/
example_test.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
//Copyright 2021 James Cote
// All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package sshdb_test
import (
"context"
"database/sql"
"fmt"
"log"
"github.com/jfcote87/sshdb"
"github.com/jfcote87/sshdb/mssql"
"github.com/jfcote87/sshdb/mysql"
"golang.org/x/crypto/ssh"
)
// ExampleNew demonstrates the package's simplest usage,
// accessing a single mysql server on a remote host where port
// 3306 is blocked but the remote host is accessible via ssh.
func ExampleNew() {
var (
// values used in connecting remote host
remoteAddr = "remote.example.com:22"
ctx, cancelFunc = context.WithCancel(context.Background())
)
defer cancelFunc()
signer, serverSigner, _ := getKeys()
exampleCfg := &ssh.ClientConfig{
User: "me",
Auth: []ssh.AuthMethod{
ssh.PublicKeys(signer),
},
HostKeyCallback: ssh.FixedHostKey(serverSigner.PublicKey()),
}
// New creates a "tunnel" for database connections.
tunnel, err := sshdb.New(exampleCfg, remoteAddr)
if err != nil {
log.Fatalf("new tunnel create failed: %v", err)
}
configs := []struct {
nettype string
dbServerAddr string
}{
{"tcp", "localhost:3306"}, // local database on remote server tcp connection
{"unix", "/tmp/mysql.sock"}, // local database on remote server via unix socket
{"tcp", "db.example.com:3306"}, // connect to db.example.com db from remote server skirt around a firewall
}
for _, cfg := range configs {
// dbServerAddr is a valid address for the db server beginning from the remote ssh server.
dsn := fmt.Sprintf("username:password@%s(%s)/schemaname?parseTime=true", cfg.nettype, cfg.dbServerAddr)
// open connector and then new DB
connector, err := tunnel.OpenConnector(mysql.TunnelDriver, dsn)
if err != nil {
log.Printf("open connector failed %s - %v", dsn, err)
continue
}
db := sql.OpenDB(connector)
defer db.Close()
// ping tests connectivity
if err := db.PingContext(ctx); err != nil {
log.Printf("%v ping failed: %v", cfg.dbServerAddr, err)
}
}
}
// ExampleNew_multiplehosts demonstrates how to connect
// multiple remote database servers via a single ssh connection
func ExampleNew_multipledbservers() {
var (
// values used in connecting remote host
remoteAddr = "remote.example.com:22"
// values used in dsn string
dbServerAddr = []string{"localsrv.example.com", "cloudsvr.example.com"}
ctx, cancelFunc = context.WithCancel(context.Background())
)
defer cancelFunc()
exampleCfg := &ssh.ClientConfig{
User: "jfcote87",
Auth: []ssh.AuthMethod{ssh.Password("my second favorite password")},
HostKeyCallback: ssh.InsecureIgnoreHostKey(),
}
tunnelCtx, err := sshdb.New(exampleCfg, remoteAddr)
if err != nil {
log.Fatalf("newDriverContext00 failed: %v", err)
}
dsn00 := fmt.Sprintf("uid=me;password=xpwd;server=%s;database=crm", dbServerAddr[0])
dsn01 := fmt.Sprintf("uid=me;password=ypwd;server=%s;database=web", dbServerAddr[1])
connector00, err := tunnelCtx.OpenConnector(mssql.TunnelDriver, dsn00)
if err != nil {
log.Fatalf("open connector failed %s - %v", dsn00, err)
}
connector01, err := tunnelCtx.OpenConnector(mssql.TunnelDriver, dsn01)
if err != nil {
log.Fatalf("open connector failed %s - %v", dsn01, err)
}
db00, db01 := sql.OpenDB(connector00), sql.OpenDB(connector01)
defer db00.Close()
defer db01.Close()
// ping tests connectivity
if err := db00.PingContext(ctx); err != nil {
log.Printf("%s ping failed: %v", "db0.example.com", err)
}
if err := db01.PingContext(ctx); err != nil {
log.Printf("%s ping failed: %v", "db1.example.com", err)
}
}
func getKeys() (ssh.Signer, ssh.Signer, error) {
signer, err := ssh.ParsePrivateKeyWithPassphrase([]byte(clientPrivateKey),
[]byte("sshdb_example"))
if err != nil {
return nil, nil, fmt.Errorf("private key parse error: %v", err)
}
serverSigner, err := ssh.ParsePrivateKey([]byte(serverPrivateKey))
if err != nil {
return nil, nil, fmt.Errorf("server private key parse error: %v", err)
}
return signer, serverSigner, nil
}
// clientPrivateKey is used to authenticate with the remote ssh server.
// This key was generated using the following command
// ssh-keygen -f ~/sshdb_client_key -t ecdsa -b 521
const clientPrivateKey = `-----BEGIN OPENSSH PRIVATE KEY-----
b3BlbnNzaC1rZXktdjEAAAAACmFlczI1Ni1jdHIAAAAGYmNyeXB0AAAAGAAAABCrg/C49e
zn3txdMKskd0JiAAAAEAAAAAEAAACsAAAAE2VjZHNhLXNoYTItbmlzdHA1MjEAAAAIbmlz
dHA1MjEAAACFBAHy1nPGt0hS9cS7ENbslUy28NC5ubYw/pdlm4w/ugkudkydOSbn+q6Hsk
VM8Q8RJP71oTOV2BWCYN5wMrk6LYTQ+QDpVDA0MHjs1ZHfhwciVZWG+RaJTZcLEhAHfUjL
v8JPPAc4q3ygNNHUJUSWY/37rJzJ0GNJU2aiEuO6dKzXb8Z1dwAAARDBuo7xtZHjwwMbS7
EExM4NzO45Hq21lPPhWcRhht90bpsG8pVG69Vb4PIo9khQDm4WfPLI/a0Vujrvj4oSckNP
ay7DN6sTtVWbfInJbt1Rm1FuECQMIakEapQmPrjQyMWHREfgM0GaRgHIAy/9KXSD1rq7co
MmWA8Jmmg7xa8wL/c/fgtB3q0vDBU5jdZHu5b/uQgdDoiZm7gwLxny0AVVWFTetpspTMbh
cmihTM9+44fHkIzhCpMzDVb8uR+FnSmjyj6GGghJtagwNm151Y3JXjNGPlRUi7VBnbE7LC
wXxGJwJo8diI8o0ew25P+n3K26eVHKfSvwljLjdBS5GeFyJE35ul4QsO2w+t0cAjj/SQ==
-----END OPENSSH PRIVATE KEY-----
`
// serverPrivateKey is used to authenticate ssh clients.
// This key was generated using the following command
// ssh-keygen -f ~/sshdb_server_key -t ecdsa -b 521
const serverPrivateKey = `-----BEGIN OPENSSH PRIVATE KEY-----
b3BlbnNzaC1rZXktdjEAAAAABG5vbmUAAAAEbm9uZQAAAAAAAAABAAAArAAAABNlY2RzYS
1zaGEyLW5pc3RwNTIxAAAACG5pc3RwNTIxAAAAhQQAaHYSCQ8ultHfdGu2LeDfR4uM8M5r
DwNziz1bwy2J57/1fZm4j4BBBNnqEXfgQwscnn2bJqoAVS8BtSKz4uA9CrEAMbTuu6FK7m
UyEKllyZ6RfdwUjBClYRsb8qvcrC2KJDNYePASZs8ufgCASEWZ2bNoZSJHooMFwOXL5q17
vDOJHqUAAAEQaQKgqGkCoKgAAAATZWNkc2Etc2hhMi1uaXN0cDUyMQAAAAhuaXN0cDUyMQ
AAAIUEAGh2EgkPLpbR33Rrti3g30eLjPDOaw8Dc4s9W8Mtiee/9X2ZuI+AQQTZ6hF34EML
HJ59myaqAFUvAbUis+LgPQqxADG07ruhSu5lMhCpZcmekX3cFIwQpWEbG/Kr3KwtiiQzWH
jwEmbPLn4AgEhFmdmzaGUiR6KDBcDly+ate7wziR6lAAAAQgDVggCI6pefB2znhtdT187I
iWZU7LTARxroTZqJzJRT3nvmu1IBV3FY0v6VXbpYoREpRfDnp8aLt2S3cPw2x8yMOwAAAA
xyb290QEpGQy1TTUcBAgMEBQY=
-----END OPENSSH PRIVATE KEY-----
`