Last active
January 27, 2017 11:17
-
-
Save netmiller/15ceb0ddf2d6e37ed7f86d5bba771f1a to your computer and use it in GitHub Desktop.
golang versio SSH-tunnelista (yhteys MySQL-kantaan; notty oletuksena)
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
package main | |
import ( | |
"os" | |
"log" | |
"net" | |
"io" | |
"io/ioutil" | |
"bytes" | |
"time" | |
// "errors" | |
"fmt" | |
"bufio" | |
"strconv" | |
s "strings" | |
"golang.org/x/crypto/ssh" | |
"crypto/md5" | |
"encoding/hex" | |
// "encoding/base64" | |
// "encoding/json" | |
) | |
type config struct { | |
server string | |
localPort int | |
user string | |
authKey string | |
debug bool | |
md5hash string | |
} | |
type Endpoint struct { | |
Host string | |
Port int | |
} | |
// palauttaa Endpoint-struct:n kentät (host:port muodossa) | |
func (ep *Endpoint) get() string { | |
return fmt.Sprintf("%s:%d", ep.Host, ep.Port) | |
} | |
type SSHtunnel struct { | |
Local *Endpoint | |
Server *Endpoint | |
Database *Endpoint | |
Config *ssh.ClientConfig | |
} | |
// luetaan config-tiedosto, josta saadaan tarvittavia muita tietoja | |
func readConf(path string) *config { | |
inFile,err := os.Open(path) | |
if err != nil { | |
log.Fatal("\n** ERROR: Tiedostoa ei löydy:",path) | |
} | |
conf := new(config) | |
defer inFile.Close() | |
scanner := bufio.NewScanner(inFile) | |
scanner.Split(bufio.ScanLines) | |
for scanner.Scan() { | |
rivi := s.ToLower(scanner.Text()) | |
// ohitetaan kommentit ja tyhjät rivit | |
if (s.HasPrefix(rivi,"#") || len(rivi)==0) { continue } | |
fmt.Println("*",rivi) | |
// haetaan asetukset ja palautetaan config_struct | |
haku := s.Split(rivi,":") | |
if s.HasPrefix(haku[0],"user") { conf.user = s.TrimSpace(haku[1]) } | |
if s.HasPrefix(haku[0],"auth") { conf.authKey = s.TrimSpace(haku[1]) } | |
if s.HasPrefix(haku[0],"serv") { conf.server = s.TrimSpace(haku[1]) } | |
if s.HasPrefix(haku[0],"localp") { conf.localPort, _ = strconv.Atoi(s.TrimSpace(haku[1])) } | |
if s.HasPrefix(haku[0],"debug") { conf.debug, _ = strconv.ParseBool(s.TrimSpace(haku[1])) } | |
} | |
return conf | |
} | |
func readAuth(conf *config) []byte { | |
// luetaan private-key tiedostosta | |
buf, err := ioutil.ReadFile(conf.authKey) | |
if err != nil { | |
fmt.Println("\n** ERROR: AuthKey_tiedostoa ei löydy !") | |
fmt.Println("** Tarkista löytyykö tiedosto : [", conf.authKey, "]") | |
fmt.Println("** OHJELMA KESKEYTETÄÄN. Tarkista virheet ja yritä uudelleen.\n") | |
log.Fatal("** EOF **") | |
} | |
return buf | |
} | |
func getMD5Hash(buf []byte) string { | |
hasher := md5.New() | |
hasher.Write(buf) | |
return hex.EncodeToString(hasher.Sum(nil)) | |
} | |
func (tunnel *SSHtunnel) Start() error { | |
listener, err := net.Listen("tcp", tunnel.Local.get()) | |
if err != nil { return err } | |
fmt.Println("\n* Winvakan tietokantayhteys valmiina!") | |
defer listener.Close() | |
for { | |
conn,err := listener.Accept() | |
if err != nil { return err } | |
// asetetaan conn objektille "SetDaedline" 0 joka toivottavasti pitää yhteyden auki ilman aikarajaa | |
conn.SetDeadline(time.Time{}) | |
go tunnel.forward(conn) | |
} | |
} | |
func (tunnel *SSHtunnel) test(cmd string) string { | |
defer func() { | |
// recover from panic if one occured. Set err to nil otherwise. | |
if (recover() != nil) { | |
fmt.Println("\n* --------------------------------------------------------------------- ") | |
fmt.Println("* ERROR: Winvaka:n tietokantayhteyttä ei saatu auki !! ") | |
fmt.Println("* Tarkista tietoliikenneyhteys ja tarvittaessa ota yhteys ylläpitoon. ") | |
fmt.Println("* --------------------------------------------------------------------- ") | |
// fmt.Println("\n") | |
log.Fatal("** EOF **") | |
// os.Exit(9) | |
} | |
}() | |
fmt.Println("* Connection test [",tunnel.Server.get(),"] ->") | |
testConn,_ := ssh.Dial("tcp", tunnel.Server.get(), tunnel.Config) | |
// if err != nil { log.Fatal("\n** ERROR: no connection ") } | |
session, _ := testConn.NewSession() | |
defer session.Close() | |
var stdoutBuf bytes.Buffer | |
session.Stdout = &stdoutBuf | |
session.Run(cmd) | |
// fmt.Println("cmd-id :", stdoutBuf.String()) | |
// "id" komennosta voisi ottaa userid:n talteen lokeja varten ? | |
fmt.Println("* Yhteys serverille OK") | |
session.Close() | |
return stdoutBuf.String() | |
} | |
func (tunnel *SSHtunnel) forward(localConn net.Conn) { | |
serverConn, err := ssh.Dial("tcp", tunnel.Server.get(), tunnel.Config) | |
if err != nil { | |
fmt.Println("* Server dial error: %s\n", err) | |
return | |
} | |
dbConn, err := serverConn.Dial("tcp", tunnel.Database.get()) | |
if err != nil { | |
fmt.Println("Database dial error: %s\n", err) | |
return | |
} | |
fmt.Println("\n* connection start ...") | |
copyConn := func(writer, reader net.Conn) { | |
_, err := io.Copy(writer, reader) | |
if err != nil { fmt.Printf("io.Copy error: %s", err) } | |
} | |
go copyConn(localConn, dbConn) | |
go copyConn(dbConn, localConn) | |
} | |
// tällä funktiolla voisi toteuttaa "known-host" logiikan mikäli tekee siihen oman ratkaisun | |
// func KeyPrint(dialAddr string, addr net.Addr, key ssh.PublicKey) error { | |
// // fmt.Printf("%s %s %s\n", strings.Split(dialAddr, ":")[0], key.Type(), base64.StdEncoding.EncodeToString(key.Marshal())) | |
// fmt.Printf("%s\n", base64.StdEncoding.EncodeToString(key.Marshal())) | |
// return nil | |
// } | |
// --- | |
func main() { | |
cfile := "./config" | |
fmt.Println() | |
log.Println("Tarkistetaan asetukset tiedostosta: [",cfile,"]") | |
// luetaan aluksi config-tiedosto | |
var conf *config = readConf(cfile) | |
fmt.Println() | |
// luetaan myös private-key mukaan tiedostosta (annettu conf-asetuksissa) | |
// var key ssh.Signer = readAuth(conf.authKey) | |
// var key ssh.Signer = readAuth(conf) | |
var keybuf []byte = readAuth(conf) | |
// tarkistetaan avaimen sisältö | |
// fmt.Printf("pub-key-luettu:\n %s \n", keybuf) | |
// lasketaan private-key avaimesta md5, jolla voisi tarkistaa jostain?? oikeellisuuden | |
// (ei ehkä ole hyötyä; joku api-key-systeemi voisi olla järkevämpi) | |
hash := getMD5Hash(keybuf) | |
conf.md5hash = hash | |
// if conf.debug { fmt.Println("* md5(key) ->",hash) } | |
var key, _ = ssh.ParsePrivateKey(keybuf) | |
// debug-ehdolla näytetään koko config-struct sisältö | |
// if conf.debug { fmt.Printf("* config -> %+v \n",conf) } | |
// out,_ := json.Marshal(conf) | |
// oo,_ := json.MarshalIndent(out, "", " ") | |
// avataan ssh-port-forward tietokantaserverille | |
localEndpoint := &Endpoint{ | |
Host: "localhost", | |
// Port: 9000, | |
Port: conf.localPort, | |
} | |
serverEndpoint := &Endpoint{ | |
// Host: "178.62.209.165", // turgon | |
Host: conf.server, | |
Port: 22, | |
} | |
// nämä ovat vielä toistaiseksi vakioita | |
dbEndpoint := &Endpoint{ | |
// Host: "localhost", | |
Host: "127.0.0.1", | |
Port: 3306, | |
} | |
sshConfig := &ssh.ClientConfig { | |
User: conf.user, | |
Auth: []ssh.AuthMethod{ ssh.PublicKeys(key) }, | |
// HostKeyCallback: KeyPrint, // funktiota ei toistaiseksi tarvita koska sille syötetään suoraan "nil" | |
HostKeyCallback: nil, | |
Timeout: 0, | |
} | |
tunnel := &SSHtunnel{ | |
Config: sshConfig, | |
Local: localEndpoint, | |
Server: serverEndpoint, | |
Database: dbEndpoint, | |
} | |
// testataan muodostuuko yhteys | |
tunnel.test("id") | |
// avataan forward-tunneli | |
tunnel.Start() | |
// log.Print("Suljetaan yhteys.\n\n") | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment