README ¶
gorexec
gorexec is a simple application written in Go that executes a shell command specified as arguments on the command line on a remote host. This was written for a course project for the Network Programming course at FMI @ Sofia University.
Architecture
It consists of a server part (where the command is executed) and a client part (which sends the command for execution). They use the libchan Golang library to communicate with each other. libchan uses a mechanism similar to Go channels to implement the connection layer of the gorexec communication protocol. It provides the functionality of channels, which operate in a way similar to websockets, or the SCTP protocol (they main PDU of the protocol is a message, which is a string of bytes with a fixed length where there is a strictly defined boundary between units). Programmers using the library may implement their own (sub)protocols and/or data structures which are wrapped in messages. Messages arrive in the same order they are sent, and can be sent in only one direction (for this reason, two channels: one for receiving, and one for sending, are used for full-duplex communication). libchan works atop a transport protocol that provides a reliable two-way bytestream (TCP, UDP, etc.) The reference implementation uses the SPDY protocol. (It can also be used over Unix sockets, websockets, HTTP, etc.)
Protocol
The server and client communicate in the following manner:
On the client side:
- A generic TCP connection is created from the client to the server to the
host provided through the
-h
command-line option on the port provided through the-p
option. If theUSE_TLS
environment variable is set, the connection is created with a TLS encryption layer. - A new SPDY stream provider (a.k.a. codec for the SPDY protocol) is created.
- A new SPDY transport layer is created (SPDY is the underlying protocol) using the provider.
- A new SPDY send-channel is created using the transport layer.
- A new receive-channel is created using the
Pipe
library function. - A new
RemoteCommand
message structure is created containing the command-line arguments passed to the client CLI utility forCmd
andArgs
respectively, and the STDIN, STDOUT and STDERR handles of the CLI utility. - The command is sent to the server through the send-channel.
- A
CommandResponse
structure is created which would contain the exit status code of the process started by the command. - The client exits with the provided exit code.
On the server side:
- The server looks for the files specified using the
TLS_CERT
andTLS_KEY
environment variables. - If it detects a TLS certificate and a key file, it loads the X.509 key pair from them and starts listening for a TCP+TLS connection. Otherwise, it tries to listen for a pure TCP connection.
- When a connection is detected, the server accepts it.
- A new SPDY stream provider (a.k.a. codec for the SPDY protocol) is created.
- A concurrent routine is started for handling the connection (so that multiple parallel connections could be serviced).
- The routine blocks until the server receives a notification that data has
been sent from the client (namely, a
RemoteCommand
containing the command, its arguments, the STDIN/STDOUT/STDERR handles, and aSender
through which the exit status of the process would be sent when it finishes execution). - When input is received, another routine is created for receiving said message.
- The message is read into a
RemoteCommand
structure. - A
Cmd
structure (for local command execution) is created using the info inside the message, and the STDOUT and STDERR inside the structure are tied to the handles inside the message. - The
Cmd
's STDIN is piped into the STDIN handle from the message. - The
Cmd
is executed using itsRun
method (i.e. synchronous execution). - When the process finishes, the STDOUT and STDERR handles inside the
RemoteCommand
are closed. - The exit status of the process is retrieved from the return value of
Run
, and is assigned to a newly createdCommandResponse
structure. - The return status is sent through the
Sender
. - The server continues with the next command and, respectively, connection.
Installation and usage (instructions for Linux)
Before installation
- Install Go using your distribution's package manager.
- Verify that Go is installed on the system and that the GOPATH environment variable is set to point to a "go" directory somewhere, and is included in PATH:
$ sudo apt-get install go
$ mkdir ~/go
$ cat >> ~/.profile
export GOPATH=$HOME/go
export PATH=$PATH:$GOPATH/bin
- Install the following dependencies:
$ go get github.com/dmcgowan/msgpack
$ go get github.com/docker/spdystream
$ go get github.com/docker/libchan
Quick installation
$ go get github.com/rgeorgiev583/gorexec/gorexec
Installation from source
- Clone the repo into
$GOPATH/src/github.com/rgeorgiev583/gorexec
. - In each of the two subdirecties there:
$ go install
Usage (when installed)
Server (IP: 10.0.0.1)
$ gorexecd 1234
Client
$ gorexec -a 10.0.0.1:1234 /bin/echo "hello"
hello
$ gorexec -a 10.0.0.1:1234 /bin/sh -c "exit 4"
$ echo $?
4
Usage (without installation)
Server
$ cd gorexecd
$ go build
$ ./gorexecd
Client
$ cd gorexec
$ go build
$ ./gorexec /bin/echo "hello"
hello
$ ./gorexec /bin/sh -c "exit 4"
$ echo $?
4
Usage with custom IP and port (without installation)
Server (IP: 10.0.0.1)
$ cd gorexecd
$ go build
$ ./gorexecd 1234
Client
$ cd gorexec
$ go build
$ ./gorexec -a 10.0.0.1:1234 /bin/echo "hello"
hello
$ ./gorexec -a 10.0.0.1:1234 /bin/sh -c "exit 4"
$ echo $?
4
Usage with TLS (without installation)
Server
$ TLS_CERT=./cert.pem TLS_KEY=./key.pem ./gorexecd
Client
$ USE_TLS=true ./gorexec /bin/echo "hello"
hello