SSH through bastion host - go

I've just started to use Go and I am trying to setup an ssh connection through a bastion host, i successfully authenticate to the bastion host, but fail on the LAN host. I've read a number of posts, the answer to this i've found very helpful. But i'm not sure what would be in that persons config. My code is as follows. I'm trying to do with with PublicKeys only and if its important i'm starting on a mac, authenticate to linux, then fail to make the second connection to another linux host. Plain ssh works fine
package main
import (
"fmt"
"golang.org/x/crypto/ssh"
"io/ioutil"
"log"
"os/user"
)
const TCP = "tcp"
const PORT = "22"
func bastionConnect(bastion string, localh string) *ssh.Client {
var usr, _ = user.Current()
var homeDir = usr.HomeDir
fmt.Printf("home is %v\n", homeDir)
key, err := ioutil.ReadFile(homeDir + "/.ssh/id_rsa")
if err != nil {
fmt.Print("i'm dying at reading ssh key")
panic(err)
}
signer, err := ssh.ParsePrivateKey(key)
if err != nil {
fmt.Print("i'm dying at parsing private key")
panic(err)
}
fmt.Printf("I'm returning public keys for %v", signer.PublicKey())
config := &ssh.ClientConfig{
User: usr.Username,
HostKeyCallback: ssh.InsecureIgnoreHostKey(),
Auth: []ssh.AuthMethod{
ssh.PublicKeys(signer),
},
}
bClient, err := ssh.Dial(TCP, bastion+":22", config)
if err != nil {
log.Fatal(err)
}
fmt.Print("passed bastion host\n")
// Dial a connection to the service host, from the bastion
conn, err := bClient.Dial(TCP, fmt.Sprintf("%s:%s", localh, PORT))
if err != nil {
log.Fatal(err)
}
ncc, chans, reqs, err := ssh.NewClientConn(conn, fmt.Sprintf("%s:%s", localh, PORT), config)
if err != nil {
fmt.Printf("Error trying to conntect to %s via bastion host\n%v\n", localh, err)
log.Fatal(err)
}
sClient := ssh.NewClient(ncc, chans, reqs)
return sClient
}
func main() {
var bastion = "jumpdev.example.org"
var lanHost = "devserver01"
bastionConnect(bastion, lanHost)
}
The last log line i see is Error trying to connect to devserver01 via bastion host with an error of
2020/02/03 14:40:17 ssh: handshake failed: ssh: unable to authenticate, attempted methods [none publickey]
Pardon all the Printfs needed to see what's up.
In the second connect could the public key config be messing it up? I have also checked out this project, but seems like overkill.

The above code was fine, i was running into an authorized_keys issue on a box that i always connect to but forgot about my local .ssh/config :(
I wanted to expand on this a bit so it was not just whoops, i messed up post. For a full bastion to lanhost agent connection, I have updated a gist here

Related

Golang SSH client error "unable to authenticate, attempted methods [none publickey], no supported methods remain"

For some reason Golang SSH client can't connect to my EC2 instance. It throws the following error:
ssh: handshake failed: ssh: unable to authenticate, attempted methods [none publickey], no supported methods remain
This is my code:
package main
import (
"fmt"
"github.com/helloyi/go-sshclient"
)
func main() {
client, err := sshclient.DialWithKey("ip:port", "ubuntu", "my_key.pem")
if err != nil {
fmt.Println(err)
return
}
out, err := client.Cmd("help").Output()
if err != nil {
fmt.Println(err)
return
}
fmt.Println(string(out))
}
What's interesting is that when I ran this code on my other computer the connection was made without any errors. So I think it must be a problem with the PC and not my code. I also tried connecting to the instance in Python using Paramiko client and it worked flawlessly. Of course I tried connecting using ssh command in CMD and MobaXTerm client - both worked. I tried using other Golang SSH client golang.org/x/crypto/ssh and it didn't work (same error).
Thank you for your help.
Apparently, it was an issue with go.mod file. Both golang.org/x/crypto and golang.org/x/sys were outdated, once I updated them it started working. Thanks #kkleejoe for your help.
assume you can ssh user#host without password, public key may be ~/.ssh/id_rsa or ~/.ssh/id_ecda
import "golang.org/x/crypto/ssh"
import "io/ioutil"
import "strconv"
func DialWithPublickey(addr string, port int, user, publickeyfile string) (*ssh.Client, error) {
key, err := ioutil.ReadFile(publickeyfile)
if err != nil {
return nil, err
}
signer, err := ssh.ParsePrivateKey(key)
if err != nil {
return nil, err
}
client, err := ssh.Dial("tcp", addr+":"+strconv.Itoa(port), &ssh.ClientConfig{
User: user,
Auth: []ssh.AuthMethod{ssh.PublicKeys(signer)},
HostKeyCallback: ssh.HostKeyCallback(func(string, net.Addr, ssh.PublicKey) error { return nil }),
})
if client == nil || err != nil {
return nil, err
}
client.SendRequest(user+"#"+addr, true, nil) // keep alive
return client, nil
}
try DialWithPublickey(host, port, user, "~/.ssh/id_rsa")

How can I make my GCloud Function open a new SSH connection to consume a SFTP server?

My setup requires a Google Function to do some stuff and upload the result to a SFTP server. I'm currently using the basic sftp and crypto/ssh packages to achieve this. Locally, after some debugging, I was able to retrieve the server's pubkey.
When deploying to GCloud nothing works, of course.
This is what handles the connection on my function
func Connect(host string, port string, user string, password string) (*ssh.Client, error) {
hostKey := getHostKey(host)
var auths []ssh.AuthMethod
// Use password authentication if provided
if password != "" {
auths = append(auths, ssh.Password(password))
}
config := &ssh.ClientConfig{
User: user,
HostKeyCallback: ssh.FixedHostKey(hostKey),
Auth: auths,
}
cipherOrder := config.Ciphers
config.Ciphers = append(cipherOrder, "aes128-cbc", "3des-cbc")
sshConn, err := ssh.Dial("tcp", host+":"+port, config)
if err != nil {
return nil, err
}
return sshConn, nil
}
func getHostKey(host string) ssh.PublicKey {
file, err := os.Open("/root/.ssh/known_hosts")
if err != nil {
fmt.Fprintf(os.Stderr, "Unable to read known_hosts file: %v\n", err)
os.Exit(1)
}
defer file.Close()
scanner := bufio.NewScanner(file)
var hostKey ssh.PublicKey
for scanner.Scan() {
fields := strings.Split(scanner.Text(), " ")
if len(fields) != 3 {
continue
}
if strings.Contains(fields[0], host) {
var err error
hostKey, _, _, _, err = ssh.ParseAuthorizedKey(scanner.Bytes())
if err != nil {
fmt.Fprintf(os.Stderr, "Error parsing %q: %v\n", fields[2], err)
os.Exit(1)
}
break
}
}
if hostKey == nil {
fmt.Fprintf(os.Stderr, "No hostkey found for %s", host)
os.Exit(1)
}
return hostKey
}
The known_hosts file doesn't exist. I don't have the pubkey of the server, but with Filezilla I can connect to it just fine.
I had to specify those cyphers because a barebone ssh hostname would return Unable to negotiate... error
Is there any other way to do this? I'm thinking about uploading my own known_hosts file but it doesn't sound like a great solution.
I was probably over engineering it.
Setting the ssh.ClientConfig like this solved the problem:
config := &ssh.ClientConfig{
User: user,
Auth: auths,
HostKeyCallback: ssh.InsecureIgnoreHostKey(),
}
Anyway I also found a nicer package to easily handle SSH connection, simplessh.
conn, _ := simplessh.ConnectWithPassword(host, user, pass)
client, _ := sftp.NewClient(conn.SSHClient)

connect to socks5 proxy with IKEv2 certificate using golang 1.10.2

I use Go 1.10.2 for my client application.
I'm trying to connect to a socks5 proxy that requires username, password and IKEv2 certificate file.
this is what I've got so far:
func socks5(proxyAdress string, url string, user string, password string) {
auth := proxy.Auth{User: user, Password: password}
if proxy, err := proxy.SOCKS5("tcp", proxyAdress, &auth, proxy.Direct); err != nil {
log.Fatalf("error: %v", proxy)
} else {
tr := &http.Transport{Dial: proxy.Dial}
// Create client
myClient := &http.Client{
Transport: tr,
}
if resp, err2 := myClient.Get(url); err2 != nil {
log.Fatalf("error: %v", err2)
} else {
if bodyBytes, err3 := ioutil.ReadAll(resp.Body); err3 != nil {
log.Fatalf("error: %v", err3)
} else {
str := string(bodyBytes)
log.Printf(str)
}
}
}
}
when I execute this function with the proper parameters
I get an error from the server that I don't have permissions to access the web page I requested. that's because I didn't specify the IKEv2 key file.
so.. first.. how do I do that ?
and 2nd... Dial is deprecated.. I'm supposed to use DialContext but has no idea how
thanks ! :)
update
the OS that I use is MacOS High Sierra 10.13.4
the problem is that for example when I execute
socks5("il7.nordvpn.com:1080", "http://ifconfig.me/ip","MY_USER","MY_PASS")
the error that I get is
2018/05/15 23:34:49 error: Get http://ifconfig.me/ip: socks connect tcp ifconfig.me:80->ifconfig.me:80: EOF
I don't know where to provide the IKEv2 certificate
I think you need to set up IPSec on your computer first.
The steps to setup IPSec
Initial configurations (only once at the first time)
Start a VPN connection
Follow here

Go SSH key doesn't work with crypto/ssh, but does manually

I generated a SSH RSA keypair with the crypto/ssh package. However, when I try to use it via a script in Go I'm getting the error:
unable to connect: ssh: handshake failed: ssh: unable to authenticate,
attempted methods [none publickey], no supported methods remain
When I connect to the remote device via CLI, it connects successfully:
ssh -i ~/.ssh/test_key_1 username#172.22.4.1
Could I possibly be using the package incorrectly?
Note: Private key does NOT have a passphrase.
package main
import (
"golang.org/x/crypto/ssh"
"io/ioutil"
"log"
)
func main() {
privateKeyFile := "/Users/username/.ssh/test_key_1"
remoteIP := "172.22.4.1:22"
user := "username"
privateKeyBytes, err := ioutil.ReadFile(privateKeyFile)
if err != nil {
log.Fatal(err)
}
key, err := ssh.ParsePrivateKey(privateKeyBytes)
if err != nil {
log.Fatal(err)
}
config := &ssh.ClientConfig{
User: user,
Auth: []ssh.AuthMethod{
// Use the PublicKeys method for remote authentication.
ssh.PublicKeys(key),
},
// using InsecureIgnoreHostKey() for testing purposes
HostKeyCallback: ssh.InsecureIgnoreHostKey(),
}
client, err := ssh.Dial("tcp", remoteIP, config)
if err != nil {
log.Fatalf("unable to connect: %v", err)
}
defer client.Close()
fmt.Println("Success!")
}
After the long process of component isolation I was finally able to verify why my Key Pairs weren't authenticating. It's due to a custom connivence package I was using that is generating slightly off Public Keys.
I've post on an open issue he had:
https://github.com/ianmcmahon/encoding_ssh/issues/1
In short:
The Public Key that was created using the EncodePublicKey() function is as below: (truncated for brevity)
ssh-rsa AAAAB3NzaC1yc2EAAAAEAAEAAQAAAgC2u3I/nbN9jcWDV..
However when running ssh-keygen -y -f id_rsa the below is created:
ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAACAQC2u3I/nbN9jcWDV...
Notice how the bits in bold are slightly different. This causes SSH authentication to not work.

SSH Reverse Tunnel with GO

I am trying to write a server / client that can help get around Firewalls / Nat Issues.
I noticed SSH has built into support for doing this already.
(http://rustyrazorblade.com/2010/03/ssh-reverse-tunnel-to-access-box-behind-firewall/)
I tried a few different SSH examples and none seem to be working. I found one project that says it implemented the Remote Port Fowarding -> https://godoc.org/dev.justinjudd.org/justin/easyssh
The Server says it is Listening for connections but I am unable to SSH from Server Machine To Client Machine. (ssh localhost 8080 on remote machine should forward to client machine.
Client ->
package main
import (
"log"
"dev.justinjudd.org/justin/easyssh"
"golang.org/x/crypto/ssh"
)
func main() {
config := &ssh.ClientConfig{
User: "test",
Auth: []ssh.AuthMethod{
ssh.Password("test"),
},
}
conn, err := easyssh.Dial("tcp", "*SSH-SERVER*:22", config)
if err != nil {
log.Fatalf("unable to connect: %s", err)
}
defer conn.Close()
err = conn.RemoteForward("0.0.0.0:8080", "127.0.0.1:22")
if err != nil {
log.Fatalf("unable to forward local port: %s", err)
}
}
Server ->
package main
import (
"fmt"
"io/ioutil"
"log"
"dev.justinjudd.org/justin/easyssh"
"golang.org/x/crypto/ssh"
)
func main() {
privateBytes, err := ioutil.ReadFile("id_rsa")
if err != nil {
log.Fatal("Failed to load private key (./id_rsa)")
}
private, err := ssh.ParsePrivateKey(privateBytes)
if err != nil {
log.Fatal("Failed to parse private key")
}
config := &ssh.ServerConfig{
PasswordCallback: func(c ssh.ConnMetadata, pass []byte) (*ssh.Permissions, error) {
if c.User() == "test" && string(pass) == "test" {
log.Printf("User logged in: %s", c.User())
return nil, nil
}
return nil, fmt.Errorf("password rejected for %s", c.User())
},
}
config.AddHostKey(private)
easyssh.HandleChannel(easyssh.SessionRequest, easyssh.SessionHandler())
easyssh.HandleChannel(easyssh.DirectForwardRequest, easyssh.DirectPortForwardHandler())
easyssh.HandleRequestFunc(easyssh.RemoteForwardRequest, easyssh.TCPIPForwardRequest)
easyssh.ListenAndServe(":22", config, nil)
}
I found a bug related to remote port forwarding in easyssh:
https://dev.justinjudd.org/justin/easyssh/src/master/tcpip.go#L107
the ssh.DiscardRequests(reqs) should be run in a separated goroutine, or else the next data transfer will not be executed.
I'm not sure that you understand how SSH tunneling works looking on your code.
You need to have SSH connectivity to the remote (server).
Then you setup SSH tunnel so local TCP:8080 port will be forwarded to the remote server TCP:8080 port using SSH connection. Actual 8080 port can be closed via firewall.
Can you connect from your client to your server with SSH?
You need to check localhost:8080 port and you need to be sure that your server 8080 port is listened by some application too.
Take a look here for some examples and theory.

Resources