sshego

package module
v1.5.9 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Mar 28, 2017 License: MIT Imports: 42 Imported by: 0

README

sshego, a usable ssh library for Go

executive summary

Google's "golang.org/x/crypto/ssh" library offers a fantastic full implementation of the ssh client and server protocols. However this library is minimalistic by design, cumbersome to figure out how to use with RSA keys, and needs additional code to support support tunneling and receiving connections as an sshd.

sshego bridges this usability gap, providing a drop-in Go library to secure your tcp connections. In places sshego can be used in preference to a virtual-private-network (VPN), for both convenience and speed. Moreover the SSH protocol's man-in-the-middle attack protection is better than a VPN in almost all cases.

usable three-factor auth in an embeddable sshd

For strong security, our embedded sshd offers three-factor auth (3FA). The three security factors are: a passphrase ("what you know"); a 6-digit Google authenticator code (TOTP/RFC 6238; "what you have": your phone); and the use of PKI in the form of 4096-bit RSA keys.

To promote strong passphrases, we follow the inspiration of https://xkcd.com/936/, and offer a user- friendly 3-word starting prompt (the user completes the sentence) to spark the user's imagination in creating a strong and memorizable passphrase. Passphrases of up to 100 characters are supported.

Although not for the super-security conscious, if desired and configured, passphrases can automatically be backed up to email (via the Mailgun email service).

On new account creation with gosshtun -adduser yourlogin, we will attempt to pop-up the QR-code on your local desktop for quick Google Authenticator setup on your phone.

introducing sshego: a gopher's do-it-yourself ssh tunneling library

sshego is a golang (Go) library for ssh tunneling (secure port forwarding). It also offers an embeddable 3-factor authentication sshd server, which can be useful for securing reverse forwards.

This means you can easily create an ssh-based vpn with 3-factor authentication requrirements: the embedded sshd requires passphrase, RSA keys, and a TOTP Google Authenticator one-time password.

In addition to the libary, gosshtun is also a command line utility (see the cmd/ subdir) that demonstrates use of the library and may prove useful on its own.

The intent of having a Go library is so that it can be used to secure (via SSH tunnel) any other traffic that your Go application would normally have to do over cleartext TCP.

While you could always run a tunnel as a separate process, by running the tunnel in process with your application, you know the tunnel is running when the process is running. It's just simpler to administer; only one thing to start instead of two.

Also this is much simpler, and much faster, than using a virtual private network (VPN). For a speed comparison, consider [1] where SSH is seen to be at least 2x faster than OpenVPN.

[1] http://serverfault.com/questions/653211/ssh-tunneling-is-faster-than-openvpn-could-it-be

In its principal use, sshego is the equivalent to using the ssh client and giving -L and/or -R. It acts like an ssh client without a remote shell; it simply tunnels other TCP connections securely.

For example,

gosshtun -listen 127.0.0.1:89  -sshd jumpy:55  -remote 10.0.1.5:80 -user alice -key ~/.ssh/id_rsa_nopw

is equivalent to

ssh -N -L 89:10.0.1.5:80 alice@jumpy -port 55

with the addendum that gosshtun requires the use of passwordless private -key file, and will never prompt you for a password at the keyboard. This makes it ideal for embedding inside your application to secure your (e.g. mysql, postgres, other cleartext) traffic. As many connections as you need will be multiplexed over the same ssh tunnel.

grain of access

If you don't trust the other users on the host where your process is running, you can also use sshego to (a) secure a direct TCP connection (see DialConfig.Dial() and the example in cli_test.go; https://github.com/glycerine/sshego/blob/master/cli_test.go#L72); or (b) forward via a file-system secured unix-domain sockets.

The first option (a) would disallow any other process (even under the same user) from multiplexing your original connection, and the second (b) would disallow any other user from accessing your tunnel, so long as you use the file-system permissions to make the unix-domain socket path inaccessible to others.

In either case, note that keys are by default stored on disk under the user's $HOME/.ssh folder, so as usual that folder should not be readable by others. Using a direct connection as in (a) in no way prevents you from starting another process (from the same executable or another) that reads the same keys and starts its own direct tcp connection.

theory of operation

gosshtun and sshego will check the sshd server's host key. We prevent MITM attacks by only allowing new servers if -new (a.k.a. SshegoConfig.AddIfNotKnown == true) is given.

When running the standalone gosshtun to test a foward, you should give -new only once at setup time.

Then the lack of -new protects you on subsequent runs, because the server's host key must match what we were given the very first time.

flags accepted, see gosshtun -h for complete list

Usage of gosshtun:
  -cfg string
        path to our config file
  -esshd string
        (optional) start an in-process embedded sshd (server),
        binding this host:port, with both RSA key and 2FA
        checking; useful for securing -revfwd connections.
  -esshd-host-db string
        (only matters if -esshd is also given) path
        to database holding sshd persistent state
        such as our host key, registered 2FA secrets, etc.
        (default "$HOME/.ssh/.sshego.sshd.db")        
  -key string
        private key for sshd login (default "$HOME/.ssh/id_rsa_nopw")
  -known-hosts string
        path to gosshtun's own known-hosts file (default
        "$HOME/.ssh/.sshego.cli.known.hosts")
  -listen string
        (forward tunnel) We listen on this host:port locally,
        securely tunnel that traffic to sshd, then send it
        cleartext to -remote. The forward tunnel is active
        if and only if -listen is given.  If host starts with
        a '/' then we treat it as the path to a unix-domain
        socket to listen on, and the port can be omitted.
  -new
        allow connecting to a new sshd host key, and store it
        for future reference. Otherwise prevent MITM attacks by
        rejecting unknown hosts.
  -quiet
        if -quiet is given, we don't log to stdout as each
        connection is made. The default is false; we log
        each tunneled connection.        
  -remote string
        (forward tunnel) After traversing the secured forward
        tunnel, -listen traffic flows in cleartext from the
        sshd to this host:port. The foward tunnel is active
        only if -listen is given too.  If host starts with a
        '/' then we treat it as the path to a unix-domain
        socket to forward to, and the port can be omitted.
  -revfwd string
        (reverse tunnel) The gosshtun application will receive
        securely tunneled connections from -revlisten on the
        sshd side, and cleartext forward them to this host:port.
        For security, it is recommended that this be 127.0.0.1:22,
        so that the sshd service on your gosshtun host
        authenticates all remotely initiated traffic.
        See also the -esshd option which can be used to
        secure the -revfwd connection as well.
        The reverse tunnel is active only if -revlisten is given
        too. (default "127.0.0.1:22")
  -revlisten string
        (reverse tunnel) The sshd will listen on this host:port,
        securely tunnel those connections to the gosshtun application,
        whence they will cleartext connect to the -revfwd address.
        The reverse tunnel is active if and only if -revlisten is given.  
  -sshd string
        The remote sshd host:port that we establish a secure tunnel to;
        our public key must have been already deployed there.
  -user string
        username for sshd login (default is $USER)
  -v    verbose debug mode
  -write-config string
        (optional) write our config to this path before doing
        connections

installation

go get github.com/glycerine/sshego/...

example use of the command

$ gosshtun -listen localhost:8888 -sshd 10.0.1.68:22 -remote 127.0.0.1:80

means the following two network hops will happen, when a local browser connects to localhost:8888

                       `gosshtun`             `sshd`
local browser ----> localhost:8888 --(a)--> 10.0.1.68:22 --(b)--> 127.0.0.1:80
  `host A`             `host A`               `host B`              `host B`

where (a) takes place inside the previously established ssh tunnel.

Connection (b) takes place over basic, un-adorned, un-encrypted TCP/IP. Of course you could always run gosshtun again on the remote host to secure the additional hop as well, but typically -remote is aimed at the 127.0.0.1, which will be internal to the remote host itself and so needs no encryption.

specifying username to login to sshd host with

The -user flag should be used if your local $USER is different from that on the sshd host.

source code for gosshtun command

See github.com/glycerine/sshego/cmd/gosshtun/main.go for the source code. This also serves as an example of how to use the library.

host key storage location (default)

~/.ssh/.sshego.known.hosts.json.snappy

prep before running

a) install your passwordless ssh-private key in ~/.ssh/id_rsa_nopw or use -key to say where it is.

b) add the corresponding public key to the user's .ssh/authorized_keys file on the sshd host.

config file format

a) see demo.env for an example

b) run gosshtun -write-config - to generate a sample config file to stdout

c) comments are allowed; lines must start with #, comments continue until end-of-line

d) fields recognized (see gosshtun -write-config - for a full list)

#
# config file for sshego:
#
SSHD_ADDR="1.2.3.4:22"
FWD_LISTEN_ADDR="127.0.0.1:8888"
FWD_REMOTE_ADDR="127.0.0.1:22"
REV_LISTEN_ADDR=""
REV_REMOTE_ADDR=""
SSHD_LOGIN_USERNAME="$USER"
SSH_PRIVATE_KEY_PATH="$HOME/.ssh/id_rsa_nopw"
SSH_KNOWN_HOSTS_PATH="$HOME/.ssh/.sshego.known.hosts"
#
# optional in-process sshd
#
EMBEDDED_SSHD_HOST_DB_PATH="$HOME/.ssh/.sshego.sshd.db"
EMBEDDED_SSHD_LISTEN_ADDR="127.0.0.1:2022"

d) special environment reads

  • The SSHD_LOGIN_USERNAME will subsitute $USER from the environment, if present.

  • The *PATH keys will substitute $HOME from the environment, if present.

MIT license

See the LICENSE file.

Author

Jason E. Aten, Ph.D.

Documentation

Overview

Package sshego is a golang libary that does secure port forwarding over ssh.

Also `gosshtun` is a command line utility included here that demonstrates use of the library; and may be useful standalone.

The intent of having a Go library is so that it can be used to secure (via SSH tunnel) any other traffic that your Go application would normally have to do over cleartext TCP.

While you could always run a tunnel as a separate process, by running the tunnel in process with your application, you know the tunnel is running when the process is running. It's just simpler to administer; only one thing to start instead of two.

Also this is much simpler, and much faster, than using a virtual private network (VPN). For a speed comparison, consider [1] where SSH is seen to be at least 2x faster than OpenVPN.

[1] http://serverfault.com/questions/653211/ssh-tunneling-is-faster-than-openvpn-could-it-be

In any case, you should realize that this is only an ssh client, and not an sshd server daemon. It is the equivalent to using the ssh client and giving `-L` and/or `-R`. If you only trust the user running your application and not your entire host, you can further restrict access by using either DialConfig.Dial() for a direct-tcpip connection, or by using the unix-domain-socket support.

For example,

gosshtun -listen 127.0.0.1:89  -sshd jumpy:55  -remote 10.0.1.5:80 -user alice -key ~/.ssh/id_rsa_nopw

is equivalent to

ssh -N -L 89:10.0.1.5:80 alice@jumpy -port 55

with the addendum that `gosshtun` requires the use of passwordless private `-key` file, and will never prompt you for a password at the keyboard. This makes it ideal for embedding inside your application to secure your (e.g. mysql, postgres, other cleartext) traffic. As many connections as you need will be multiplexed over the same ssh tunnel.

theory of operation

We check the sshd server's host key. We prevent MITM attacks by only allowing new servers if `-new` is given.

You should give `-new` only once at setup time.

Then the lack of `-new` can protect you on subsequent runs, because the server's host key must match what we were given the first time.

options

$ gosshtun -h
Usage of gosshtun:
 -cfg string
       path to our config file
 -esshd string
       (optional) start an in-process embedded sshd (server),
       binding this host:port, with both RSA key and 2FA
       checking; useful for securing -revfwd connections.
 -esshd-host-db string
       (only matters if -esshd is also given) path
       to database holding sshd persistent state
       such as our host key, registered 2FA secrets, etc.
       (default "$HOME/.ssh/.sshego.sshd.db")
 -key string
       private key for sshd login (default "$HOME/.ssh/id_rsa_nopw")
 -known-hosts string
       path to gosshtun's own known-hosts file (default
       "$HOME/.ssh/.sshego.cli.known.hosts")
 -listen string
       (forward tunnel) We listen on this host:port locally,
       securely tunnel that traffic to sshd, then send it
       cleartext to -remote. The forward tunnel is active
       if and only if -listen is given.  If host starts with
       a '/' then we treat it as the path to a unix-domain
       socket to listen on, and the port can be omitted.
 -new
       allow connecting to a new sshd host key, and store it
       for future reference. Otherwise prevent MITM attacks by
       rejecting unknown hosts.
 -quiet
       if -quiet is given, we don't log to stdout as each
       connection is made. The default is false; we log
       each tunneled connection.
 -remote string
       (forward tunnel) After traversing the secured forward
       tunnel, -listen traffic flows in cleartext from the
       sshd to this host:port. The foward tunnel is active
       only if -listen is given too.  If host starts with
       a '/' then we treat it as the path to a unix-domain
       socket to forward to, and the port can be omitted.
 -revfwd string
       (reverse tunnel) The gosshtun application will receive
       securely tunneled connections from -revlisten on the
       sshd side, and cleartext forward them to this host:port.
       For security, it is recommended that this be 127.0.0.1:22,
       so that the sshd service on your gosshtun host
       authenticates all remotely initiated traffic.
       See also the -esshd option which can be used to
       secure the -revfwd connection as well.
       The reverse tunnel is active only if -revlisten is given
       too. (default "127.0.0.1:22")
 -revlisten string
       (reverse tunnel) The sshd will listen on this host:port,
       securely tunnel those connections to the gosshtun application,
       whence they will cleartext connect to the -revfwd address.
       The reverse tunnel is active if and only if -revlisten is given.
 -sshd string
       The remote sshd host:port that we establish a secure tunnel to;
       our public key must have been already deployed there.
 -user string
       username for sshd login (default is $USER)
 -v    verbose debug mode
 -write-config string
       (optional) write our config to this path before doing
       connections
$

example use of the command

$ gosshtun -listen localhost:8888 -sshd 10.0.1.68:22 -remote 127.0.0.1:80

means the following two network hops will happen, when a local browser connects to localhost:8888

                       `gosshtun`             `sshd`
local browser ----> localhost:8888 --(a)--> 10.0.1.68:22 --(b)--> 127.0.0.1:80
  `host A`             `host A`               `host B`              `host B`

where (a) takes place inside the previously established ssh tunnel.

Connection (b) takes place over basic, un-adorned, un-encrypted TCP/IP. Of course you could always run `gosshtun` again on the remote host to secure the additional hop as well, but typically -remote is aimed at the 127.0.0.1, which will be internal to the remote host itself and so needs no encryption.

Index

Constants

View Source
const MUX_C_OPEN_FWD = 0x10000006 // 268435462
View Source
const ProgramName = "sshego-library"
View Source
const Verbose bool = false

Verbose can be set to true for debug output. For production builds it should be set to false, the default.

Variables

View Source
var DelUserCmd = []byte("01DELUSER___")
View Source
var DelUserCmdStr = string(DelUserCmd)
View Source
var DelUserReplyFailed = []byte("01REPLY_FAIL")
View Source
var DelUserReplyOK = []byte("01REPLY_OK__")
View Source
var ErrCouldNotAquirePort = fmt.Errorf("could not acquire " +
	"our -xport before the deadline")
View Source
var GIT_BRANCH string
View Source
var GO_VERSION string
View Source
var LAST_GIT_COMMIT_HASH string
View Source
var NEAREST_GIT_TAG string
View Source
var NewUserCmd = []byte("00NEWUSER___")
View Source
var NewUserCmdStr = string(NewUserCmd)
View Source
var NewUserReply = []byte("00REPLY_____")

Functions

func AddUserAndExit added in v1.5.7

func AddUserAndExit(cfg *SshegoConfig)

func CryptoRandBytes

func CryptoRandBytes(n int) []byte

func CryptoRandInt64

func CryptoRandInt64() int64

Use crypto/rand to get an random int64

func CryptoRandNonNegInt

func CryptoRandNonNegInt(n int64) int64

func DelUserAndExit added in v1.5.7

func DelUserAndExit(cfg *SshegoConfig)

func DialRemoteUnixDomain added in v1.4.0

func DialRemoteUnixDomain(c *ssh.Client, udpath string) (net.Conn, error)

DialRemoteUnixDomain initiates a connection to udpath from the remote host using c as the ssh client. Here udpath is a unixDomain socket path in the remote filesystem. The resulting connection has a zero LocalAddr() and RemoteAddr().

func Fingerprint

func Fingerprint(k ssh.PublicKey) string

Fingerprint performs a SHA256 BASE64 fingerprint of the PublicKey, similar to OpenSSH. See: https://anongit.mindrot.org/openssh.git/commit/?id=56d1c83cdd1ac

func GenRSAKeyPair

func GenRSAKeyPair(rsaFile string, bits int, email string) (priv *rsa.PrivateKey, sshPriv ssh.Signer, err error)

GenRSAKeyPair generates an RSA keypair of length bits. If rsa_file != "", we write the private key to rsa_file and the public key to rsa_file + ".pub". If rsa_file == "" the keys are not written to disk.

func GenRSAKeyPairCrypt

func GenRSAKeyPairCrypt(rsaFile string, bits int, password string) (priv *rsa.PrivateKey, sshPriv ssh.Signer, err error)

TODO: Finish this-- specified but password based encryption not implemented. GenRSAKeyPairCrypt generates an RSA keypair of length bits. If rsa_file != "", we write the private key to rsa_file and the public key to rsa_file + ".pub". If rsa_file == "" the keys are not written to disk. The private key is encrypted with the password.

func GetAvailPort added in v1.5.7

func GetAvailPort() (net.Listener, int)

GetAvailPort asks the OS for an unused port, returning a bound net.Listener and the port number to which it is bound. The caller should Close() the listener when it is done with the port.

func GetExternalIP

func GetExternalIP() string

GetExternalIP tries to determine the external IP address used on this host.

func IsAlreadyBound added in v1.5.7

func IsAlreadyBound(addr string) bool

func IsRoutableIPv4

func IsRoutableIPv4(ip string) bool

IsRoutableIPv4 returns true if the string in ip represents an IPv4 address that is not private. See http://en.wikipedia.org/wiki/Private_network#Private_IPv4_address_spaces for the numeric ranges that are private. 127.0.0.1, 192.168.0.1, and 172.16.0.1 are examples of non-routables IP addresses.

func KnownHostsEqual

func KnownHostsEqual(a, b *KnownHosts) (bool, error)

KnownHostsEqual compares two instances of KnownHosts structures for equality.

func LoadRSAPrivateKey

func LoadRSAPrivateKey(path string) (privkey ssh.Signer, err error)

LoadRSAPrivateKey reads a private key from path on disk.

func LoadRSAPrivateKeyCrypt

func LoadRSAPrivateKeyCrypt(path string, password string) (privkey ssh.Signer, err error)

TODO: Finish this-- specified but password based encryption not implemented. LoadRSAPrivateKey reads a private key from path on disk.

func LoadRSAPublicKey

func LoadRSAPublicKey(path string) (pubkey ssh.PublicKey, err error)

LoadRSAPublicKey reads a public key from path on disk. By convention these keys end in '.pub', but that is not verified here.

func MakeAndMoveToTempDir added in v1.5.7

func MakeAndMoveToTempDir() (origdir string, tmpdir string)

func PromptForPassword

func PromptForPassword(username string) (pw string, err error)

PromptForPassword ask

func RSAToSSHPublicKey

func RSAToSSHPublicKey(pubkey *rsa.PublicKey) []byte

RSAToSSHPublicKey convert an RSA Public Key to the SSH authorized_keys format.

func RandomString added in v1.4.0

func RandomString(n int) string

func ScryptHash

func ScryptHash(password string) []byte

func SetWinsize

func SetWinsize(fd uintptr, w, h uint32)

SetWinsize sets the size of the given pty.

func SourceVersion added in v1.4.6

func SourceVersion() string

SourceVersion returns the git source code version this code was built from.

func StartBackgroundTestTcpServer added in v1.5.7

func StartBackgroundTestTcpServer(serverDone chan bool, payloadByteCount int, confirmationPayload string, confirmationReply string, tcpSrvLsn net.Listener)

func TempDirCleanup added in v1.5.7

func TempDirCleanup(origdir string, tmpdir string)

func TestCreateNewAccount added in v1.5.7

func TestCreateNewAccount(srvCfg *SshegoConfig) (mylogin, toptPath, rsaPath, pw string, err error)

func UnencPingPong added in v1.5.7

func UnencPingPong(dest, confirmationPayload, confirmationReply string, payloadByteCount int)

func VerifyClientServerExchangeAcrossSshd added in v1.5.7

func VerifyClientServerExchangeAcrossSshd(channelToTcpServer net.Conn, confirmationPayload, confirmationReply string, payloadByteCount int)

func WaitUntilAddrAvailable added in v1.5.7

func WaitUntilAddrAvailable(addr string, dur time.Duration, tries int) int

waitUntilAddrAvailable returns -1 if the addr was alays unavailable after tries sleeps of dur time. Otherwise it returns the number of tries it took. Between attempts we wait 'dur' time before trying again.

Types

type AddrHostPort

type AddrHostPort struct {
	Title          string
	Addr           string
	Host           string
	Port           int64
	UnixDomainPath string
	Required       bool
}

AddrHostPort is used to specify tunnel endpoints.

func (*AddrHostPort) ParseAddr

func (a *AddrHostPort) ParseAddr() error

ParseAddr fills Host and Port from Addr, breaking Addr apart at the ':' using net.SplitHostPort()

type AtomicUserMap

type AtomicUserMap struct {
	U map[string]*User
	// contains filtered or unexported fields
}

func NewAtomicUserMap

func NewAtomicUserMap() *AtomicUserMap

func (*AtomicUserMap) DecodeMsg

func (z *AtomicUserMap) DecodeMsg(dc *msgp.Reader) (err error)

DecodeMsg implements msgp.Decodable

func (*AtomicUserMap) Del

func (m *AtomicUserMap) Del(key string)

func (*AtomicUserMap) EncodeMsg

func (z *AtomicUserMap) EncodeMsg(en *msgp.Writer) (err error)

EncodeMsg implements msgp.Encodable

func (*AtomicUserMap) Get

func (m *AtomicUserMap) Get(key string) *User

func (*AtomicUserMap) Get2

func (m *AtomicUserMap) Get2(key string) (*User, bool)

func (*AtomicUserMap) MarshalMsg

func (z *AtomicUserMap) MarshalMsg(b []byte) (o []byte, err error)

MarshalMsg implements msgp.Marshaler

func (*AtomicUserMap) Msgsize

func (z *AtomicUserMap) Msgsize() (s int)

Msgsize returns an upper bound estimate of the number of bytes occupied by the serialized message

func (*AtomicUserMap) Set

func (m *AtomicUserMap) Set(key string, val *User)

func (*AtomicUserMap) String added in v1.5.9

func (m *AtomicUserMap) String() string

func (*AtomicUserMap) UnmarshalMsg

func (z *AtomicUserMap) UnmarshalMsg(bts []byte) (o []byte, err error)

UnmarshalMsg implements msgp.Unmarshaler

type AuthState

type AuthState struct {
	HostKey ssh.Signer
	OneTime *TOTP

	AuthorizedKeysMap map[string]bool

	PrivateKeys map[string]interface{}
	Signers     map[string]ssh.Signer
	PublicKeys  map[string]ssh.PublicKey

	Cert *ssh.Certificate
}

AuthState holds the authorization information that doesn't change after startup; each fresh PerAttempt gets a pointer to one of these. Currently assumes only one user.

func NewAuthState

func NewAuthState(w *TOTP) *AuthState

func (*AuthState) LoadHostKey

func (a *AuthState) LoadHostKey(path string) error

func (*AuthState) LoadPublicKeys

func (a *AuthState) LoadPublicKeys(authorizedKeysPath string) error

type BasicAddress added in v1.5.5

type BasicAddress struct {
	// contains filtered or unexported fields
}

Address satisfies the net.Addr interface, which BasicListener.Addr() returns.

func (*BasicAddress) Network added in v1.5.5

func (a *BasicAddress) Network() string

Network returns the name of the network, "sshego"

func (*BasicAddress) String added in v1.5.5

func (a *BasicAddress) String() string

String returns the string form of the address.

type BasicListener added in v1.5.5

type BasicListener struct {
	// contains filtered or unexported fields
}

BasicListener satifies the net.Listener interface

func (*BasicListener) Accept added in v1.5.5

func (b *BasicListener) Accept() (net.Conn, error)

Accept and Listen support BasicServer functionality. Accept waits for and returns the next connection to the listener.

func (*BasicListener) Addr added in v1.5.5

func (b *BasicListener) Addr() net.Addr

Addr returns the listener's network address.

func (*BasicListener) Close added in v1.5.5

func (b *BasicListener) Close() error

Close closes the listener. Any blocked Accept operations will be unblocked and return errors.

type BasicServer added in v1.5.5

type BasicServer struct {
	// contains filtered or unexported fields
}

BasicServer configures a simple embedded sshd server that only expects RSA key (or other) based authentication, and doesn't expect TOTP or passphase. This makes it suitable for using with unattended systems / to replace a TLS server.

func NewBasicServer added in v1.5.5

func NewBasicServer(cfg *SshegoConfig) *BasicServer

NewBasicServer in listen.go provides net.Listen() compatibility for running an embedded sshd. It refactors server.go's Start() into Listen() and Accept().

func (*BasicServer) Close added in v1.5.5

func (b *BasicServer) Close() error

Close closes bind to the port that the server has.

func (*BasicServer) Listen added in v1.5.5

func (bs *BasicServer) Listen(laddr string) (*BasicListener, error)

Listen announces on the local network address laddr. The syntax of laddr is "host:port", like "127.0.0.1:2222". We listen on a TCP port.

type CommandRecv

type CommandRecv struct {
	Done chan bool
	// contains filtered or unexported fields
}

func (*CommandRecv) Start

func (cr *CommandRecv) Start() error

type ConnectionAlert added in v1.5.5

type ConnectionAlert struct {
	PortOne  chan ssh.Channel
	ShutDown chan bool
}

type DialConfig

type DialConfig struct {

	// ClientKnownHostsPath is the path to the file
	// on client's disk that holds the known server keys.
	ClientKnownHostsPath string

	// cached to avoid a disk read, we only read
	// from ClientKnownHostsPath if KnownHosts is nil.
	// Users of DialConfig can leave this nil and
	// simply provide ClientKnownHostsPath. It is
	// exposed in case you need to invalidate the
	// cache and start again.
	KnownHosts *KnownHosts

	// the username to login under
	Mylogin string

	// the path on the local file system (client side) from
	// which to read the client's RSA private key.
	RsaPath string

	// the time-based one-time password configuration
	TotpUrl string

	// Pw is the passphrase
	Pw string

	// which sshd to connect to, host and port.
	Sshdhost string
	Sshdport int64

	// DownstreamHostPort is the host:port string of
	// the tcp address to which the sshd should forward
	// our connection to.
	DownstreamHostPort string

	// TofuAddIfNotKnown, for maximum security,
	// should be always left false and
	// the host key database should be configured
	// manually. If true, the client trusts the server's
	// provided key and stores it, which creates
	// vulnerability to a MITM attack.
	//
	// TOFU stands for Trust-On-First-Use.
	//
	// If set to true, Dial() will stoop
	// after storing a new key, or error
	// out if the key is already known.
	// In either case, a 2nd attempt at
	// Dial is required wherein on the
	// TofuAddIfNotKnown is set to false.
	//
	TofuAddIfNotKnown bool

	// DoNotUpdateSshKnownHosts prevents writing
	// to the file given by ClientKnownHostsPath, if true.
	DoNotUpdateSshKnownHosts bool

	Verbose bool

	// test only; see SshegoConfig
	TestAllowOneshotConnect bool
}

DialConfig provides Dial() with what it needs in order to establish an encrypted and authenticated ssh connection.

func (*DialConfig) Dial

func (dc *DialConfig) Dial() (net.Conn, *ssh.Client, error)

Dial is a convenience method for contacting an sshd over tcp and creating a direct-tcpip encrypted stream. It is a simple two-step sequence of calling dc.Cfg.SSHConnect() and then calling Dial() on the returned *ssh.Client.

PRE: dc.Cfg.KnownHosts should already be instantiated. To prevent MITM attacks, the host we contact at hostport must have its server key must be already in the KnownHosts.

dc.RsaPath is the path to the our (the client's) rsa private key file.

dc.DownstreamHostPort is the host:port tcp address string to which the sshd should forward our connection after successful authentication.

type Esshd

type Esshd struct {
	Halt idem.Halter
	// contains filtered or unexported fields
}

Esshd is our embedded sshd server, running from inside this libary.

func (*Esshd) Listen added in v1.5.5

func (e *Esshd) Listen(bs *BasicServer) (*BasicListener, error)

Listen and Accept support BasicServer functionality. Together, Listen() then Accept() replace Start().

func (*Esshd) NewCommandRecv

func (e *Esshd) NewCommandRecv() *CommandRecv

func (*Esshd) Start

func (e *Esshd) Start()

func (*Esshd) Stop

func (e *Esshd) Stop() error

type Forwarder

type Forwarder struct {
	// contains filtered or unexported fields
}

Forwarder represents one bi-directional forward (sshego to sshd) tcp connection.

func NewForward

func NewForward(cfg *SshegoConfig, sshClientConn *ssh.Client, fromBrowser net.Conn) *Forwarder

NewForward is called to produce a Forwarder structure for each new forward connection.

type HostDb

type HostDb struct {

	// Users: key is MyLogin; value is *User.
	Users *AtomicUserMap

	HostPrivateKeyPath string
	// contains filtered or unexported fields
}

func (*HostDb) AddUser

func (h *HostDb) AddUser(mylogin, myemail, pw, issuer, fullname string) (toptPath, qrPath, rsaPath string, err error)

func (*HostDb) DecodeMsg

func (z *HostDb) DecodeMsg(dc *msgp.Reader) (err error)

DecodeMsg implements msgp.Decodable

func (*HostDb) DelUser

func (h *HostDb) DelUser(mylogin string) error

func (*HostDb) EncodeMsg

func (z *HostDb) EncodeMsg(en *msgp.Writer) (err error)

EncodeMsg implements msgp.Encodable

func (*HostDb) MarshalMsg

func (z *HostDb) MarshalMsg(b []byte) (o []byte, err error)

MarshalMsg implements msgp.Marshaler

func (*HostDb) Msgsize

func (z *HostDb) Msgsize() (s int)

Msgsize returns an upper bound estimate of the number of bytes occupied by the serialized message

func (*HostDb) String added in v1.5.9

func (h *HostDb) String() string

func (*HostDb) UnmarshalMsg

func (z *HostDb) UnmarshalMsg(bts []byte) (o []byte, err error)

UnmarshalMsg implements msgp.Unmarshaler

func (*HostDb) UserExists

func (h *HostDb) UserExists(mylogin string) bool

UserExists is used by sshego/cmd/gosshtun/main.go

func (*HostDb) ValidEmail

func (h *HostDb) ValidEmail(myemail string) (bool, error)

func (*HostDb) ValidLogin

func (h *HostDb) ValidLogin(login string) (bool, error)

type HostState

type HostState int

HostState recognizes host keys are legitimate or impersonated, new, banned, or consitent with what we've seen before and so OK.

const AddedNew HostState = 4

AddedNew means the -new flag was given and we allowed the addition of a new host-key for the first time.

const Banned HostState = 1

Banned means the host has been marked as forbidden.

const KnownOK HostState = 2

KnownOK means the host key matches one we have previously allowed.

const KnownRecordMismatch HostState = 3

KnownRecordMismatch means we have a records for this IP/host-key, but either the IP or the host-key has varied and so it could be a Man-in-the-middle attack.

const Unknown HostState = 0

Unknown means we don't have a matching stored host key.

func (HostState) String

func (s HostState) String() string

type KnownHosts

type KnownHosts struct {
	Hosts map[string]*ServerPubKey

	// FilepathPrefix doesn't have the .json.snappy suffix on it.
	FilepathPrefix string

	PersistFormatSuffix string

	// PersistFormat is the format indicator
	PersistFormat KnownHostsPersistFormat

	// NoSave means we don't touch the files we read from
	NoSave bool
	// contains filtered or unexported fields
}

KnownHosts represents in Hosts a hash map of host identifier (ip or name) and the corresponding public key for the server. It corresponds to the ~/.ssh/known_hosts file.

func LoadSshKnownHosts

func LoadSshKnownHosts(path string) (*KnownHosts, error)

LoadSshKnownHosts reads a ~/.ssh/known_hosts style file from path, see the SSH_KNOWN_HOSTS FILE FORMAT section of http://manpages.ubuntu.com/manpages/zesty/en/man8/sshd.8.html or the local sshd(8) man page.

func NewKnownHosts

func NewKnownHosts(filepath string, format KnownHostsPersistFormat) (*KnownHosts, error)

NewKnownHosts creats a new KnownHosts structure. filepathPrefix does not include the PersistFormat suffix. If filepathPrefix + defaultFileFormat() exists as a file on disk, then we read the contents of that file into the new KnownHosts.

The returned KnownHosts will remember the filepathPrefix for future saves.

func (*KnownHosts) AddNeeded

func (h *KnownHosts) AddNeeded(addIfNotKnown, allowOneshotConnect bool, hostname string, remote net.Addr, strPubBytes string, key ssh.PublicKey, record *ServerPubKey) (HostState, *ServerPubKey, error)

func (*KnownHosts) Close

func (h *KnownHosts) Close()

Close cleans up and prepares for shutdown. It calls h.Sync() to write the state to disk.

func (*KnownHosts) HostAlreadyKnown

func (h *KnownHosts) HostAlreadyKnown(hostname string, remote net.Addr, key ssh.PublicKey, pubBytes []byte, addIfNotKnown bool, allowOneshotConnect bool) (HostState, *ServerPubKey, error)

HostAlreadyKnown checks the given host details against our known hosts file.

func (*KnownHosts) Sync

func (h *KnownHosts) Sync() (err error)

Sync writes the contents of the KnownHosts structure to the file h.FilepathPrefix + h.PersistFormat (for json/gob); to just h.FilepathPrefix for "ssh_known_hosts" format.

type KnownHostsPersistFormat

type KnownHostsPersistFormat int

type LoginRecord

type LoginRecord struct {
	FirstTm       time.Time
	LastTm        time.Time
	SeenCount     int64
	AcceptedCount int64
	PubFinger     string
}

LoginRecord is per public key.

func (*LoginRecord) DecodeMsg

func (z *LoginRecord) DecodeMsg(dc *msgp.Reader) (err error)

DecodeMsg implements msgp.Decodable

func (*LoginRecord) EncodeMsg

func (z *LoginRecord) EncodeMsg(en *msgp.Writer) (err error)

EncodeMsg implements msgp.Encodable

func (*LoginRecord) MarshalMsg

func (z *LoginRecord) MarshalMsg(b []byte) (o []byte, err error)

MarshalMsg implements msgp.Marshaler

func (*LoginRecord) Msgsize

func (z *LoginRecord) Msgsize() (s int)

Msgsize returns an upper bound estimate of the number of bytes occupied by the serialized message

func (LoginRecord) String

func (r LoginRecord) String() string

func (*LoginRecord) UnmarshalMsg

func (z *LoginRecord) UnmarshalMsg(bts []byte) (o []byte, err error)

UnmarshalMsg implements msgp.Unmarshaler

type MailgunConfig

type MailgunConfig struct {

	// MAILGUN_DOMAIN
	Domain string

	// MAILGUN_PUBLIC_API_KEY
	PublicApiKey string

	//MAILGUN_SECRET_API_KEY
	SecretApiKey string
}

MailgunConfig sets up sending backup emails through Mailgun. See https://mailgun.com.

func (*MailgunConfig) DefineFlags

func (c *MailgunConfig) DefineFlags(fs *flag.FlagSet)

DefineFlags should be called before myflags.Parse().

func (*MailgunConfig) LoadConfig

func (c *MailgunConfig) LoadConfig(path string) error

LoadConfig reads configuration from a file, expecting KEY=value pair on each line; values optionally enclosed in double quotes.

func (*MailgunConfig) SaveConfig

func (c *MailgunConfig) SaveConfig(fd io.Writer) error

SaveConfig writes the config structs to the given io.Writer

func (*MailgunConfig) SendEmail

func (c *MailgunConfig) SendEmail(senderEmail, subject, plain, html, recipEmail string) (string, error)

func (*MailgunConfig) ValidateConfig

func (c *MailgunConfig) ValidateConfig() error

ValidateConfig should be called after myflags.Parse().

type PerAttempt

type PerAttempt struct {
	PublicKeyOK bool
	OneTimeOK   bool

	User   *User
	State  *AuthState
	Config *ssh.ServerConfig
	// contains filtered or unexported fields
}

PerAttempt holds the auth state that should be reset anew on each login attempt; plus a pointer to the invariant State.

func NewPerAttempt

func NewPerAttempt(s *AuthState, cfg *SshegoConfig) *PerAttempt

func (*PerAttempt) AuthLogCallback

func (a *PerAttempt) AuthLogCallback(conn ssh.ConnMetadata, method string, err error)

func (*PerAttempt) KeyboardInteractiveCallback

func (a *PerAttempt) KeyboardInteractiveCallback(conn ssh.ConnMetadata, challenge ssh.KeyboardInteractiveChallenge) (*ssh.Permissions, error)

func (*PerAttempt) NoteLogin

func (a *PerAttempt) NoteLogin(user *User, now time.Time, conn ssh.ConnMetadata)

func (*PerAttempt) PerConnection

func (a *PerAttempt) PerConnection(nConn net.Conn, ca *ConnectionAlert) error

func (*PerAttempt) PublicKeyCallback

func (a *PerAttempt) PublicKeyCallback(c ssh.ConnMetadata, providedPubKey ssh.PublicKey) (perm *ssh.Permissions, rerr error)

func (*PerAttempt) SetTripleConfig

func (a *PerAttempt) SetTripleConfig()

SetTripleConfig establishes an a.State.Config that requires *both* public key and one-time password validation.

func (*PerAttempt) SetupAuthRequirements

func (a *PerAttempt) SetupAuthRequirements()

type Reverse

type Reverse struct {
	// contains filtered or unexported fields
}

Reverse represents one bi-directional (initiated at sshd, tunneled to sshego) tcp connection.

type ServerPubKey

type ServerPubKey struct {
	Hostname string

	// HumanKey is a serialized and readable version of Key, the key for Hosts map in KnownHosts.
	HumanKey     string
	ServerBanned bool

	// reading ~/.ssh/known_hosts
	Markers                  string
	Hostnames                string
	SplitHostnames           map[string]bool
	Keytype                  string
	Base64EncodededPublicKey string
	Comment                  string
	Port                     string
	LineInFileOneBased       int

	// if AlreadySaved, then we don't need to append.
	AlreadySaved bool
	// contains filtered or unexported fields
}

ServerPubKey stores the RSA public keys for a particular known server. This structure is stored in KnownHosts.Hosts.

func (*ServerPubKey) AddHostPort

func (prior *ServerPubKey) AddHostPort(hp string)

type SshegoConfig

type SshegoConfig struct {
	ConfigPath string

	SSHdServer    AddrHostPort // the sshd host we are logging into remotely.
	LocalToRemote TunnelSpec
	RemoteToLocal TunnelSpec

	Debug bool

	AddIfNotKnown bool

	Username             string
	PrivateKeyPath       string
	ClientKnownHostsPath string

	KnownHosts *KnownHosts

	WriteConfigOut string

	// if -write-config is all we are doing
	WriteConfigOnly bool

	Quiet bool

	Esshd                  *Esshd
	EmbeddedSSHdHostDbPath string
	EmbeddedSSHd           AddrHostPort // optional local sshd, embedded.

	HostDb *HostDb

	AddUser string
	DelUser string

	SshegoSystemMutexPortString string
	SshegoSystemMutexPort       int

	MailCfg MailgunConfig

	// allow less than 3FA
	// Not recommended, but possible.
	SkipTOTP       bool
	SkipPassphrase bool
	SkipRSA        bool

	BitLenRSAkeys int

	DirectTcp   bool
	ShowVersion bool

	//
	// ==== testing support ====
	//
	Origdir, Tempdir string

	// TestAllowOneshotConnect is
	// a convenience for testing.
	//
	// If we discover and add a new
	// sshd host key on this first,
	// allow the connection to
	// continue on without
	// erroring out -- the gosshtun
	// command line does this to
	// teach users safe run
	// practices, but under test
	// it is just annoying.
	TestAllowOneshotConnect bool

	TestingModeNoWait bool

	Mut sync.Mutex
}

SshegoConfig is the top level, main config

func GenTestConfig added in v1.5.7

func GenTestConfig() (c *SshegoConfig, releasePorts func())

func NewSshegoConfig

func NewSshegoConfig() *SshegoConfig

func (*SshegoConfig) DefineFlags

func (c *SshegoConfig) DefineFlags(fs *flag.FlagSet)

DefineFlags should be called before myflags.Parse().

func (*SshegoConfig) GenAuthString

func (cfg *SshegoConfig) GenAuthString() string

func (*SshegoConfig) LoadConfig

func (c *SshegoConfig) LoadConfig(path string) error

LoadConfig reads configuration from a file, expecting KEY=value pair on each line; values optionally enclosed in double quotes.

func (*SshegoConfig) NewEsshd

func (cfg *SshegoConfig) NewEsshd() *Esshd

NewEsshd sets cfg.Esshd with a newly constructed Esshd. does NewHostDb() internally.

func (*SshegoConfig) NewHostDb

func (cfg *SshegoConfig) NewHostDb() error

func (*SshegoConfig) SSHConnect

func (cfg *SshegoConfig) SSHConnect(h *KnownHosts, username string, keypath string, sshdHost string, sshdPort int64, passphrase string, toptUrl string) (*ssh.Client, error)

SSHConnect is the main entry point for the gosshtun library, establishing an ssh tunnel between two hosts.

passphrase and toptUrl (one-time password used in challenge/response) are optional, but will be offered to the server if set.

func (*SshegoConfig) SaveConfig

func (c *SshegoConfig) SaveConfig(fd io.Writer) error

SaveConfig writes the config structs to the given io.Writer

func (*SshegoConfig) StartNewReverse

func (cfg *SshegoConfig) StartNewReverse(sshClientConn *ssh.Client, fromRemote net.Conn) (*Reverse, error)

StartNewReverse is invoked once per reverse connection made to generate a new Reverse structure.

func (*SshegoConfig) StartupForwardListener

func (cfg *SshegoConfig) StartupForwardListener(sshClientConn *ssh.Client) error

StartupForwardListener is called when a forward tunnel is the be listened for.

func (*SshegoConfig) StartupReverseListener

func (cfg *SshegoConfig) StartupReverseListener(sshClientConn *ssh.Client) error

StartupReverseListener is called when a reverse tunnel is requested, to listen and tunnel those connections.

func (*SshegoConfig) TcpClientUserAdd

func (cfg *SshegoConfig) TcpClientUserAdd(user *User) (toptPath, qrPath, rsaPath string, err error)

func (*SshegoConfig) TcpClientUserDel

func (cfg *SshegoConfig) TcpClientUserDel(user *User) error

func (*SshegoConfig) ValidateConfig

func (c *SshegoConfig) ValidateConfig() error

ValidateConfig should be called after myflags.Parse().

type TOTP

type TOTP struct {
	UserEmail string
	Issuer    string
	Key       *otp.Key
	QRcodePng []byte
}

func NewTOTP

func NewTOTP(userEmail, issuer string) (w *TOTP, err error)

func (*TOTP) IsValid

func (w *TOTP) IsValid(passcode string, mylogin string) bool

func (*TOTP) LoadFromFile

func (w *TOTP) LoadFromFile(path string) error

func (*TOTP) SaveToFile

func (w *TOTP) SaveToFile(path string) (secretPath, qrPath string, err error)

func (*TOTP) String

func (w *TOTP) String() string

type TcpPort

type TcpPort struct {
	Port int
	Lsn  net.Listener
	// contains filtered or unexported fields
}

func (*TcpPort) Lock

func (t *TcpPort) Lock(limitMsec int) error

func (*TcpPort) Unlock

func (t *TcpPort) Unlock()

type TestSetup added in v1.5.7

type TestSetup struct {
	CliCfg  *SshegoConfig
	SrvCfg  *SshegoConfig
	Mylogin string
	RsaPath string
	Totp    string
	Pw      string
}

func MakeTestSshClientAndServer added in v1.5.7

func MakeTestSshClientAndServer(startEsshd bool) *TestSetup

type TunnelSpec

type TunnelSpec struct {
	Listen AddrHostPort
	Remote AddrHostPort
}

TunnelSpec represents either a forward or a reverse tunnel in SshegoConfig.

type User

type User struct {
	MyEmail    string
	MyFullname string
	MyLogin    string

	PublicKeyPath  string
	PrivateKeyPath string
	TOTPpath       string
	QrPath         string

	Issuer string

	SeenPubKey map[string]LoginRecord

	ScryptedPassword []byte
	ClearPw          string // only on network, never on disk.
	TOTPorig         string

	FirstLoginTime time.Time
	LastLoginTime  time.Time
	LastLoginAddr  string
	IPwhitelist    []string
	DisabledAcct   bool
	// contains filtered or unexported fields
}

User represents a user authorized to login to the embedded sshd.

func NewUser

func NewUser() *User

func (*User) DecodeMsg

func (z *User) DecodeMsg(dc *msgp.Reader) (err error)

DecodeMsg implements msgp.Decodable

func (*User) EncodeMsg

func (z *User) EncodeMsg(en *msgp.Writer) (err error)

EncodeMsg implements msgp.Encodable

func (*User) MarshalMsg

func (z *User) MarshalMsg(b []byte) (o []byte, err error)

MarshalMsg implements msgp.Marshaler

func (*User) MatchingHashAndPw

func (user *User) MatchingHashAndPw(password string) bool

func (*User) Msgsize

func (z *User) Msgsize() (s int)

Msgsize returns an upper bound estimate of the number of bytes occupied by the serialized message

func (*User) RestoreTotp

func (user *User) RestoreTotp()

func (*User) String added in v1.5.9

func (u *User) String() string

func (*User) UnmarshalMsg

func (z *User) UnmarshalMsg(bts []byte) (o []byte, err error)

UnmarshalMsg implements msgp.Unmarshaler

type Winsize

type Winsize struct {
	Height uint16
	Width  uint16
	// contains filtered or unexported fields
}

Winsize stores the Height and Width of a terminal.

Directories

Path Synopsis
cmd
manual-test-client
a simple test client.
a simple test client.
manual-test-recv
a simple test receiver (server) to check that your direct-tcpip connections are coming through the sshd.
a simple test receiver (server) to check that your direct-tcpip connections are coming through the sshd.
manual-unixdomain-client
a simple test client.
a simple test client.
manual-unixdomain-recv
a simple test receiver (server) to check that your direct-tcpip connections are coming through the sshd.
a simple test receiver (server) to check that your direct-tcpip connections are coming through the sshd.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL