🤘 The native golang ssh client to execute your commands over ssh connection. 🚀🚀

Overview

Golang SSH Client.

Fast and easy golang ssh client module.

Goph is a lightweight Go SSH client focusing on simplicity!

Installation ❘ Features ❘ Usage ❘ Examples ❘ License

🚀   Installation and Documentation

go get github.com/melbahja/goph

You can find the docs at go docs.

🤘   Features

  • Easy to use and simple API.
  • Supports known hosts by default.
  • Supports connections with passwords.
  • Supports connections with private keys.
  • Supports connections with protected private keys with passphrase.
  • Supports upload files from local to remote.
  • Supports download files from remote to local.
  • Supports connections with ssh agent (Unix systems only).
  • Supports adding new hosts to known_hosts file.
  • Supports file system operations like: Open, Create, Chmod...

📄   Usage

Run a command via ssh:

package main

import (
	"log"
	"fmt"
	"github.com/melbahja/goph"
)

func main() {

	// Start new ssh connection with private key.
	auth, err := goph.Key("/home/mohamed/.ssh/id_rsa", "")
	if err != nil {
		log.Fatal(err)
	}

	client, err := goph.New("root", "192.1.1.3", auth)
	if err != nil {
		log.Fatal(err)
	}

	// Defer closing the network connection.
	defer client.Close()

	// Execute your command.
	out, err := client.Run("ls /tmp/")

	if err != nil {
		log.Fatal(err)
	}

	// Get your output as []byte.
	fmt.Println(string(out))
}

🔐 Start Connection With Protected Private Key:

auth, err := goph.Key("/home/mohamed/.ssh/id_rsa", "you_passphrase_here")
if err != nil {
	// handle error
}

client, err := goph.New("root", "192.1.1.3", auth)

🔑 Start Connection With Password:

client, err := goph.New("root", "192.1.1.3", goph.Password("you_password_here"))

☛ Start Connection With SSH Agent (Unix systems only):

auth, err := goph.UseAgent()
if err != nil {
	// handle error
}

client, err := goph.New("root", "192.1.1.3", auth)

⤴️ Upload Local File to Remote:

err := client.Upload("/path/to/local/file", "/path/to/remote/file")

⤵️ Download Remote File to Local:

err := client.Download("/path/to/remote/file", "/path/to/local/file")

☛ Execute Bash Commands:

out, err := client.Run("bash -c 'printenv'")

☛ Execute Bash Command With Env Variables:

out, err := client.Run(`env MYVAR="MY VALUE" bash -c 'echo $MYVAR;'`)

🥪 Using Goph Cmd:

Goph.Cmd struct is like the Go standard os/exec.Cmd.

// Get new `Goph.Cmd`
cmd, err := client.Command("ls", "-alh", "/tmp")

if err != nil {
	// handle the error!
}

// You can set env vars, but the server must be configured to `AcceptEnv line`.
cmd.Env = []string{"MY_VAR=MYVALUE"}

// Run you command.
err = cmd.Run()

🗒️ Just like os/exec.Cmd you can run CombinedOutput, Output, Start, Wait, and ssh.Session methods like Signal...

📂 File System Operations Via SFTP:

You can easily get a SFTP client from Goph client:

sftp, err := client.NewSftp()

if err != nil {
	// handle the error!
}

file, err := sftp.Create("/tmp/remote_file")

file.Write([]byte(`Hello world`))
file.Close()

🗒️ For more file operations see SFTP Docs.

🥙   Examples

See Examples.

🤝   Missing a Feature?

Feel free to open a new issue, or contact me.

📘   License

Goph is provided under the MIT License.

Issues
  • Add: AddKnownHost func

    Add: AddKnownHost func

    add new function: AddKnownHost to add new hosts to known_hosts file and some improvements

    • resolves #2
    opened by melbahja 19
  • Making defaultPath evaluation lazy

    Making defaultPath evaluation lazy

    Would you consider wrapping defaultPath in a function? The issue is on Windows environments HOME is not set by default. Simple solution would be setting it manually when the program starts:

    os.Setenv("HOME", os.Getenv("USERPROFILE"))
    

    but it doesn't get recognized by goph since it is evaluated beforehand.

    opened by xxzefgh 5
  • upload multiple files with same sftp connection

    upload multiple files with same sftp connection

    I noticed that when trying to upload in sequence multiple files (calling the Upload function for one file after the other) at some point Linux hosts will kick you out with a "administratively prohibited: open failed". Is there a way around it?

    opened by iotgopher 4
  • Prompt to Add to knownhosts on new connections

    Prompt to Add to knownhosts on new connections

    This is an ssh.HostKeyCallback function I came up with and probably needs to be cleaned up. This was apart of an internal package I created for ssh connections. It doesn't print fingerprints as it should. But it was ignored because the tool I was creating was an internal project.

    func knownHostKey(hostname string, remote net.Addr, key ssh.PublicKey) error {
    	knownFile := os.ExpandEnv("$HOME/.ssh/known_hosts")
    	_, err := os.Stat(knownFile)
    	if errors.Is(err, os.ErrNotExist) {
    		f, ferr := os.OpenFile(knownFile, os.O_CREATE, 0600)
    		if ferr != nil {
    			return ferr
    		}
    		f.Close()
    	}
    	callback, err := knownhosts.New(knownFile)
    	if err != nil {
    		return err
    	}
    	err = callback(hostname, remote, key)
    	if err == nil {
    		return nil
    	}
    	var keyErr *knownhosts.KeyError
    	if errors.As(err, &keyErr) && len(keyErr.Want) == 0 {
    		r := bufio.NewReader(os.Stdin)
    		fmt.Print("Unknown Host. Would you like to add it [y/N]: ")
    		reply, rerr := r.ReadString('\n')
    		if rerr != nil {
    			return rerr
    		}
    		reply = strings.Replace(reply, "\n", "", -1)
    		reply = strings.Replace(reply, "\r", "", -1)
    		if strings.ToLower(reply) != "y" {
    			return err
    		}
    		f, ferr := os.OpenFile(knownFile, os.O_WRONLY|os.O_APPEND, 0600)
    		if ferr != nil {
    			return ferr
    		}
    		defer f.Close()
    		knownHost := knownhosts.Normalize(remote.String())
    		_, err = f.WriteString(knownhosts.Line([]string{knownHost}, key) + "\n")
    		if err != nil {
    			return err
    		}
    		return nil
    	}
    	return err
    }
    

    I can add a PR if this is something you would like to support in this package.

    opened by dixonwille 3
  • Issue with known hosts

    Issue with known hosts

    I believe this is not completely correct. I have have a host (NAME) I should be saving that first and then the remote IP or both but right now it only save the remote IP.. but the Check for Knownhost seems to use the host rather than IP so I get a mistmatched

    https://github.com/melbahja/goph/blob/f67d58a8a072ab20293fd0813895e7c3130aecf0/hosts.go#L99

    Also I was looking at https://www.godoc.org/golang.org/x/crypto/ssh#HostKeyCallback and it seems that It receives the hostname as passed to Dial or NewClientConn

    I think the logic should check if host and remote are the same otherwise write the host if not matching

    opened by epinault 3
  • Custom

    Custom "port" ?

    There are a few places (including command line flags) where it is mentioned but I don't see it being actually used. Is it implemented? If yes, how is it set?

    opened by xpufx 3
  • Fix issue around adding incorrect KnownHost

    Fix issue around adding incorrect KnownHost

    Host should always be check against remote as it could be a name or an IP. but if the name is passed then it won t match the remote IP and therefore saved the incorrect entry in the known host

    opened by epinault 2
  • SSH NewSession Can Hang Indefinitely

    SSH NewSession Can Hang Indefinitely

    See https://github.com/golang/go/issues/26643

    The relevant fix can be seen here: https://github.com/cybozu-go/cke/pull/81/files

    I hit this using the latest goph client on a remote machine with a badly behaving SSH Server.

    Workaround is to timeout the channel in which the goph calls are being executed.

        select {
        case result := <- someChannel:
            fmt.Println(result)
        case <- time.After(timeout * time.Second):
            fmt.Println("ssh new session was hung")
        }
    
    opened by amcrn 2
  • Need a function that automatically adds host to known_hosts.

    Need a function that automatically adds host to known_hosts.

    Are you considering adding a function that automatically adds hosts to known_hosts for goph.Config.Callback?

    For some controllable internal networks, we basically do not need to consider man-in-the-middle attacks when connecting to the server, and we use other solutions to control user login and record behavior.

    opened by nightan42643 2
  • Remove unnecessary panics

    Remove unnecessary panics

    Hello!

    There are several points in the package where unnecessary panic was thrown. It will be better to have an error instead.

    opened by dshemin 1
  • Authentication failiure using ssh-agent

    Authentication failiure using ssh-agent

    I'm trying to connect to server using code from the examples, but the authentication fails without further information:

    package main
    
    import (
    	"fmt"
    	"log"
    
    	"github.com/melbahja/goph"
    )
    
    func main() {
    
    	auth, err := goph.UseAgent()
    
    	if err != nil {
    		log.Fatal(err)
    	}
    
    	client, err := goph.New("root", "kfbox.public", auth)
    
    	if err != nil {
    		log.Fatal(err)
    	}
    
    	// Defer closing the network connection.
    	defer client.Close()
    
    	// Execute your command.
    	out, err := client.Run("ls /tmp/")
    
    	if err != nil {
    		log.Fatal(err)
    	}
    
    	// Get your output as []byte.
    	fmt.Println(string(out))
    }
    
    $ go run main.go
    2021/11/10 17:29:47 ssh: handshake failed: ssh: unable to authenticate, attempted methods [none publickey], no supported methods remain
    exit status 1
    
    $  ssh [email protected]
    Last login: Wed Nov 10 17:23:22 2021 from 89.0.11.182
    [email protected]:~/ >
    

    As you can see the agent authenticates without problems when using plain ssh. Is there any way to figure out why this is failing?

    opened by pinpox 4
  • added ReadFile and WriteFile functions

    added ReadFile and WriteFile functions

    Hey, thanks for merging my previous PR, I have yet another one :)

    This one adds a support for reading and writing remote files to/from memory.

    opened by antonsergeyev 0
  • Add support to connect via ProxyJump option

    Add support to connect via ProxyJump option

    Hi would you consider adding option to connect to target via jump host? My flow is that I have setup ProxyJump option in my SSH config and I do all my SSH connection via bastion host. I would like some simple API to specify the jump host connection.

    opened by phandox 0
  • No support for commands with sudo

    No support for commands with sudo

    When trying to execute command with sudo, e.g. sudo sleep 3, I receive the error:

    sudo: no tty present and no askpass program specified
    
    2021/08/20 17:52:03 Process exited with status 1
    exit status 1
    

    Example code:

    package main
    
    import (
    	"log"
    	"fmt"
    	"github.com/melbahja/goph"
    )
    
    func main() {
    
    	// Start new ssh connection with password
    	auth := goph.Password("12345")
    
    	client, err := goph.New("testssh", "172.16.1.10", auth)
    	if err != nil {
    		log.Fatal(err)
    	}
    
    	// Defer closing the network connection.
    	defer client.Close()
    
    	// Execute your command.
    	out, err := client.Run("sudo sleep 3")
    
    	if err != nil {
    	        fmt.Println(string(out))
    		log.Fatal(err)
    	}
    
    	// Get your output as []byte.
    	fmt.Println(string(out))
    }
    
    opened by agentram 2
  • example question

    example question

    When switch on "passphrase" as following. flag.BoolVar(&passphrase, "passphrase", true, "ask for private key passphrase.")

    it will output error message as following: `E:\goph-1.2.1\examples\goph>gophex.exe Enter Private Key Passphrase: panic: The handle is invalid.

    goroutine 1 [running]: main.askPass(0xb3ae66, 0x1e, 0x0, 0x0) E:/goph-1.2.1/examples/goph/main.go:156 +0x1a5 main.getPassphrase(...) E:/goph-1.2.1/examples/goph/main.go:168 main.main() E:/goph-1.2.1/examples/goph/main.go:114 +0x4d7` No chance to input Passphrase

    opened by hongnod 0
  • panic: sftp:

    panic: sftp: "Failure" (SSH_FX_FAILURE)

    Here is my code:

    	err = client.Upload("./test.txt", "/root/")
    	if err != nil {
    		panic(err)
    	}
    

    it will raise error(sftp: "Failure" (SSH_FX_FAILURE)) when remotePath is not full path. It is necessary to adapt the path without a file name

    opened by aiyijing 3
  • support pkcs#11 and ms capi ?

    support pkcs#11 and ms capi ?

    Can hardware based PKI be added? For example, yubikey opensc feitan, these manufacturers provide PKCS # 11 library For windows, you can use Microsoft CryptoAPI to access the supported USBKEY.

    opened by jaychouwii 0
  • ssh -D tunneling

    ssh -D tunneling

    Any guide on ssh -D tunneling and proxycommand? Does this project support these features?

    opened by pigscanflyyyy 2
Owner
Mohamed El Bahja
Developer, FOSS Enthusiast.
Mohamed El Bahja
wire protocol for multiplexing connections or streams into a single connection, based on a subset of the SSH Connection Protocol

qmux qmux is a wire protocol for multiplexing connections or streams into a single connection. It is based on the SSH Connection Protocol, which is th

Jeff Lindsay 160 Nov 17, 2021
The devs are over here at devzat, chat over SSH!

Devzat Where are the devs at? Devzat! Devzat is chat over SSH Try it out: ssh sshchat.hackclub.com Add this to ~/.ssh/config: Host chat HostName s

Ishan Goel 340 Dec 2, 2021
red-tldr is a lightweight text search tool, which is used to help red team staff quickly find the commands and key points they want to execute, so it is more suitable for use by red team personnel with certain experience.

Red Team TL;DR English | 中文简体 What is Red Team TL;DR ? red-tldr is a lightweight text search tool, which is used to help red team staff quickly find t

倾旋 145 Nov 15, 2021
Golang `net/rpc` over SSH using installed SSH program

Golang net/rpc over SSH using installed SSH program This package implements a helper functions to launch an RPC client and server. It uses the install

null 0 Nov 5, 2021
Send network packets over a TCP or UDP connection.

Packet is the main class representing a single network message. It has a byte code indicating the type of the message and a []byte type payload.

Aero 58 Nov 19, 2021
Extended ssh-agent which supports git commit signing over ssh

ssh-agentx ssh-agentx Rationale Requirements Configuration ssh-agentx Configuration ssh-gpg-signer Linux Windows Signing commits after configuration T

Wim 3 May 18, 2021
Make TCP connection storm between server and client for benchmarking network stuff

Make TCP connection storm between server and client for benchmarking network stuff

Masahiro Nagano 2 Nov 14, 2021
Chisel is a fast TCP/UDP tunnel, transported over HTTP, secured via SSH.

Chisel is a fast TCP/UDP tunnel, transported over HTTP, secured via SSH. Single executable including both client and server. Written in Go (golang). Chisel is mainly useful for passing through firewalls, though it can also be used to provide a secure endpoint into your network.

Jaime Pillora 6.5k Dec 2, 2021
Serve traffic (HTTP/gRPC) over SSH using Domain Sockets

Serve On SSH Introduction There is often a need to offer services for administrative purposes on servers or even for microservices that are running on

John Doak 6 Nov 12, 2021
Chat over SSH.

ssh-chat Custom SSH server written in Go. Instead of a shell, you get a chat prompt. Demo Join the party: $ ssh ssh.chat Please abide by our project'

Andrey Petrov 4.5k Dec 5, 2021
A golang library about socks5, supports all socks5 commands. That Provides server and client and easy to use. Compatible with socks4 and socks4a.

socks5 This is a Golang implementation of the Socks5 protocol library. To see in this SOCKS Protocol Version 5. This library is also compatible with S

chenhao zhang 31 Nov 12, 2021
Golang connection multiplexing library

Yamux Yamux (Yet another Multiplexer) is a multiplexing library for Golang. It relies on an underlying connection to provide reliability and ordering,

HashiCorp 1.6k Nov 25, 2021
Package socket provides a low-level network connection type which integrates with Go's runtime network poller to provide asynchronous I/O and deadline support. MIT Licensed.

socket Package socket provides a low-level network connection type which integrates with Go's runtime network poller to provide asynchronous I/O and d

Matt Layher 24 Nov 2, 2021
WebSocket Connection Smuggler

ws-smuggler ws-smuggler is websocket connection smuggling testing tool. It is similar to the this project, but it has been rewritten based on the web

HAHWUL 35 Nov 17, 2021
ConnPool is a thread safe connection pool for net.Conn interface.

ConnPool is a thread safe connection pool for net.Conn interface. It can be used to manage and reuse connections.

Burak Sezer 117 Nov 19, 2021
This package helps establish a websocket connection to the bilibili streaming server.

biliStreamClient This package helps establish a websocket connection to the bilibili streaming server. bilibili直播弹幕的WebSocket协议分析请参考:https://blog.csdn

JINGWEI ZHANG 19 Aug 27, 2021
Uses the Finger user information protocol to open a TCP connection that makes a request to a Finger server

Finger Client This client uses the Finger user information protocol to open a TCP connection that makes a request to a Finger server. Build and Run Ru

Linda Xiao 0 Oct 7, 2021
ZheTian Powerful remote load and execute ShellCode tool

ZheTian ZheTian Powerful remote load and execute ShellCode tool 免杀shellcode加载框架 命令详解 -u:从远程服务器加载base64混淆后的字节码。 -r:从本地文件内读。 -s:读取无修改的原始文件,只能从本地加载 -o:参数

一曲成殇 165 Nov 26, 2021