Documentation
¶
Overview ¶
Package sshdb provides database connections to tunnel through an ssh connection to a remove server
Index ¶
- func RegisterDriver(key string, driver Driver)
- type ConfigError
- type Datasource
- type Dialer
- type DialerFunc
- type Driver
- type Tunnel
- func (tun *Tunnel) Close() error
- func (tun *Tunnel) ConnCount() int
- func (tun *Tunnel) DialContext(ctx context.Context, _, addr string) (net.Conn, error)
- func (tun *Tunnel) IgnoreSetDeadlineRequest(val bool)
- func (tun *Tunnel) OpenConnector(tunnelDriver Driver, dataSourceName string) (driver.Connector, error)
- type TunnelConfig
Examples ¶
Constants ¶
This section is empty.
Variables ¶
This section is empty.
Functions ¶
func RegisterDriver ¶
RegisterDriver associates a database's Driver interface with the key. This allows a TunnelConfig to handle multiple database types per host connection.
Types ¶
type ConfigError ¶
ConfigError used to describe errors when opening DBs based upon a Config
func (*ConfigError) Error ¶
func (ce *ConfigError) Error() string
Error make the ConfigError an error
func (*ConfigError) Unwrap ¶
func (ce *ConfigError) Unwrap() error
Unwrap returns the internal error
type Datasource ¶
type Datasource struct { DriverName string `yaml:"driver_name" json:"driver_name,omitempty"` ConnectionString string `yaml:"dsn" json:"dsn,omitempty"` // tests use this parameter Queries []string `yaml:"queries,omitempty" json:"queries,omitempty"` }
Datasource defines a database connection using the Driver name and a connection string for use by the underlying sql driver. The DriverName must be registered or the TunnelConfig.OpenDB will return an error.
func (Datasource) Driver ¶
func (cd Datasource) Driver() (Driver, error)
Driver returns the Driver associated with the ConnDefinition.DriverName. Will return error if the name was not associated using the RegisterDriver func.
type DialerFunc ¶
DialerFunc allows a func to fulfill the Dialer interface.
func (DialerFunc) DialContext ¶
DialContext calls the underlying dialerfunc.
type Driver ¶
type Driver interface { OpenConnector(dialer Dialer, dsn string) (driver.Connector, error) Name() string }
Driver creates a Connector for a specific database type using the dialer passed from a Tunnel and a dsn string that defines a connection from the remote server to the desired database.
This package includes drivers for mysql, mssql, postgress (v3 and v4).
type Tunnel ¶
type Tunnel struct {
// contains filtered or unexported fields
}
Tunnel manages an ssh client connections and creates and tracks db connections made through the client
func New ¶
func New(clientConfig *ssh.ClientConfig, remoteHostPort string) (*Tunnel, error)
New returns a Tunnel based upon the ssh clientConfig for creating new connectors/connections via an ssh client connection. The tunnel can host multiple db connections to different database servers. the remoteHostPort defines the remote ssh server address and must be in the form "host:port", "host%zone:port", [host]:port" or "[host%zone]:port". See func net.Dial for a more detailed description of the hostport format.
Example ¶
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.
package main import ( "context" "database/sql" "fmt" "log" "github.com/jfcote87/sshdb" "github.com/jfcote87/sshdb/mysql" "golang.org/x/crypto/ssh" ) func main() { 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) } } } 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----- `
Output:
Example (Multipledbservers) ¶
ExampleNew_multiplehosts demonstrates how to connect multiple remote database servers via a single ssh connection
package main import ( "context" "database/sql" "fmt" "log" "github.com/jfcote87/sshdb" "github.com/jfcote87/sshdb/mssql" "golang.org/x/crypto/ssh" ) func main() { 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) } }
Output:
func (*Tunnel) Close ¶
Close safely resets the tunnel. If calling func has already locked tunnel.m, it should call reset directly.
func (*Tunnel) DialContext ¶
DialContext creates an ssh client connection to the addr. sshdb drivers must use this func when creating driver.Connectors. You may use this func to establish "raw" connections to a remote service.
func (*Tunnel) IgnoreSetDeadlineRequest ¶
IgnoreSetDeadlineRequest exists because the ssh client package does not support deadlines and returns an error if attempting to set a deadline. If existing code contains setdeadline calls, pass true to this functions, and the tunnel ignore deadline requests.
func (*Tunnel) OpenConnector ¶
func (tun *Tunnel) OpenConnector(tunnelDriver Driver, dataSourceName string) (driver.Connector, error)
OpenConnector fulfills the driver DriverContext interface and returns a new db connection via the ssh client connection. The dataSourceName should follow rules of the base database and must create the connection as if connecting from the remote ssh server.
type TunnelConfig ¶
type TunnelConfig struct { // address of remote server must be in the form "host:port", "host%zone:port", // "[host]:port" or "[host%zone]:port". See func net.Dial for a description of // the hostport parameter. HostPort string `yaml:"hostport,omitempty" json:"hostport,omitempty"` // login name for the remote ssh connection UserID string `yaml:"user_id,omitempty" json:"user_id,omitempty"` // password to use with UserID. May be blank if using keys. Pwd string `yaml:"pwd,omitempty" json:"pwd,omitempty"` // file containing a PEM version of the private key used for authenticating the ssh session ClientKeyFile string `yaml:"client_key_file,omitempty" json:"client_key_file,omitempty"` // string containing PEM of private key. May not use ClientKeyFile and ClientKey simultaneously ClientKey string `yaml:"client_key,omitempty" json:"client_key,omitempty"` // if private key is phrase protected, set this to password phrase. Otherwise leave blank ClientKeyPwd string `yaml:"client_key_pwd,omitempty" json:"client_key_pwd,omitempty"` // file containing public key for validating remote host. ServerPublicKeyFile and ServerPublicKey may // not be used simultaneously. ServerPublicKeyFile string `yaml:"server_public_key_file,omitempty" json:"server_public_key_file,omitempty"` // string containing public key definition. If no public key specified, InsecureIgnoreHostKey is assumed ServerPublicKey string `yaml:"server_public_key,omitempty" json:"server_public_key,omitempty"` // IgnoreDeadlines tells the tunnel to ignore deadline requests as the ssh tunnel does not implement IgnoreDeadlines bool `yaml:"ignore_deadlines,omitempty" json:"ignore_deadlines,omitempty"` // a map of ConnDefinitions for each db connection using the tunnel. Each dsn will return a corresponding *sql.DB Datasources map[string]Datasource `yaml:"datasources,omitempty" json:"datasources,omitempty"` // contains filtered or unexported fields }
TunnelConfig describes an ssh connection to a remote host and the databases accessed via the connection. See the example_config_test.go file for examples.
func (*TunnelConfig) DB ¶ added in v0.5.3
func (tc *TunnelConfig) DB(dbname string) (*sql.DB, error)
DB returns an open DB based up the datasource defined by the name in the TunnelConfig
func (*TunnelConfig) DatabaseMap ¶
func (tc *TunnelConfig) DatabaseMap() (map[string]*sql.DB, error)
DatabaseMap returns *sql.DBs returns a map of *sql.DBs based upon the DatabaseMap field. Either all dbs defined in the config are returned with no error or no db is returned if an error occurs. Tunnels datasources connect in a lazy fashion so that the connections are not until a database command is called.
Directories
¶
Path | Synopsis |
---|---|
Package internal contains a LoadTunnelConfig function that reads either a json or yaml representation of a sshdb.TunnelConfig
|
Package internal contains a LoadTunnelConfig function that reads either a json or yaml representation of a sshdb.TunnelConfig |
Package mssql provides for mssql connection via the sshdb package
|
Package mssql provides for mssql connection via the sshdb package |
Package mysql provide for mysql connections via the sshdb package
|
Package mysql provide for mysql connections via the sshdb package |
Package oracle provide for mysql connections via the sshdb package
|
Package oracle provide for mysql connections via the sshdb package |
Package pgx provides for ssh postgres connections via the github.com/jackc/pgx package
|
Package pgx provides for ssh postgres connections via the github.com/jackc/pgx package |
Package pgxv4 provides for ssh postgres connections via the "github.com/jackc/pgx/v4"
|
Package pgxv4 provides for ssh postgres connections via the "github.com/jackc/pgx/v4" |