Git Product home page Git Product logo

sshmuxd's Introduction

sshmuxd Go Report Card

A SSH "jump host" style proxy, based off the https://github.com/kennylevinsen/sshmux library.

So, why not just a jump host? Well, if it's just you and no one else needing access, go ahead. If you, however, want to give more than one person SSH access through your public IP on port N (N often being 22), then you might want something with a bit more access control. Sure, you can make really complicated SSH configs that limit a lot of things for the other users, but they'll always be able to poke around more than you want them to, and it'll be a pain in the butt to maintain.

Thinking it could be done simpler, sshmux and sshmuxd got written. It allows you to have a proxy that will only permit forwarding to user-specific servers, regardless of method. No other poking around is possible, and no having to allow actual login for anyone to the server running sshmuxd.

Installation

The safest way to install sshmuxd is via git:

git clone --recursive https://github.com/kennylevinsen/sshmuxd.git $GOPATH/src/github.com/kennylevinsen/sshmuxd
cd $GOPATH/src/github.com/kennylevinsen/sshmuxd
go install

Dependencies are vendored as git submodules.

What does it do?

It acts like a regular SSH server, waiting for either session channel requests (regular ssh) or direct tcp connection requests (ssh -W).

If it gets a regular session channel request, it will figure out what servers the user is allowed to connect to. If the user is only permitted access to one server, it writes out which server it is connecting to, and connects immediately. If the user is permitted access to multiple servers, it will present the user with an interactive prompt, asking which server the user wishes to connect to.

If it gets a direct tcp connection request, it will simply check if this connection is permitted for the user, and if yes, execute the connection.

Just show me what it looks like!

Using the "regular ssh"-mode with interactive selection (that is, more than one permitted remote host for that user):

$ ssh sshmux.example.com
Welcome to sshmux, kennylevinsen
    [0] server1.example.com:22
    [1] server2.example.com:22
    [2] secret.example.com:65432
Please select remote server:

If you then enter a number, it'll look like this:

Please select remote server: 1
Connecting to server2.example.com:22
$ hostname
server2.example.com

If there were only one permitted host, sshmuxd will skip right to showing "Connecting to...". In direct tcp mode (ssh -W), you don't see any difference at all.

But agent forwarding is dangerous!

In the general sense, yes. If you don't just ignore SSH's warnings about changed host keys, then you can be sure that you're connecting to the real host. That means that, to abuse agents in the common sense, the root user must be compromised on that machine. The root user will then be able to sign things with our private key by accessing the agent socket that got forwarded. The result is the same as if the root user of your local machine got compromised, with a private key that is password protected very well, but the agent had the key unlocked, signing requests as it is told.

With sshmux, agent forwarding isn't handled as a socket, but in-memory. Depending on OS, this doesn't necessarily protect you against an evil root, due to peculiar interfaces such as /dev/mem, but it sure does increase the barrier of entry: Rather than just reading an easily found socket, you need to inspect arbitrary process memory, finding the right component and interfacing with it accordingly.

A good note, however, is that if you are concerned about the hosts you log into having arbitrary process memory or root login compromised, you shouldn't really log in to them at all, but take them offline immediately and use a serial console (virtual or physical) to salvage what is necessary, followed by a total wipe of the machine. Using them, or even just leaving them online in this state, will not bring you any good.

With this, I am not saying that agent forwarding isn't dangerous. Don't use agent-forwarding to arbitrary machines you don't trust. If you have these concerns, you can use ssh -W, which provides guarantees of security against any issues with the jump host (but not the remote host). ssh -W as ProxyCommand in ssh_config is also much more convenient for hosts you log into often, rather than having to make interactive selections.

Personally, I wish that ssh agents would ask the user before signing. This way, agent forwarding could be used a bit more relaxed, as users would be prompted with what and who is trying to sign before accepting. If signing is attempted without you using it, you could simply deny it. Blindly signing things is a bad idea.

But what's ssh -W?

ssh -W asks the SSH server to make a raw TCP connection, and forward stdin/stdout of the local client over the ssh connection to the raw TCP connection. How do you use that to jump hosts? With ProxyCommand! Put the following in your ~/.ssh/config (see the ssh_config manpage):

Host server1.example.com
	ProxyCommand ssh -W %h:%p sshmux.example.com

Followed by running ssh from your command-line:

ssh server1.example.com

You can also do this directly on the command-line, without ssh_config, with:

ssh -oProxyCommand="ssh -W %h:%p sshmux.example.com" server1.example.com

This technique works is the general approach to jump hosts, and not related to sshmux. sshmux simply implements it with fine-grained controls. For more info, see the the wiki page (https://github.com/kennylevinsen/sshmuxd/wiki/ProxyCommand) or the ssh manpage.

Limitations

sshmux, and by extension, sshmuxd, can only forward normal sessions (ssh'ing directly to sshmuxd without a ProxyCommand) if agent forwarding is enabled. This is because your normal session authenticates to sshmux, but sshmux then has to authenticate you with the remote host, requiring a additional access to your agent. sshmux will, however, not forward your agent to the final remote host. Doing this is simple if wanted, but I have yet to decide on how this is toggled.

Please note that the sftp and scp clients bundled with openssh cannot use normal session forwarding thus you must use a ProxyCommand for them. If you want them to work normal session forwarding, try to revive this very old bug report about it: https://bugzilla.mindrot.org/show_bug.cgi?id=831.

Using a "ssh -W" ProxyCommand circumvents this limitation, both for ssh and sftp/scp, and also bypasses the interactive server selection, as the client will inform sshmux of the wanted target directly. If the target is permitted, the user will be connected. This also provides more protection for the paranoid, as the connection to the final host is encrypted end-to-end, rather than being plaintext in the memory of sshmux.

Configuration

sshmuxd requires a configuration file named sshmuxd.json/.yml in one of the following places:

  • Working dir
    • $HOME/.sshmuxd/
    • /etc/sshmuxd/
    • or pass the path on command line using --config filename.json

The format of the file is as follows (note that, due to the presence of comments, this is not actually a valid JSON file. Remove comments before use, or refer to sshmuxd.json). You can also use YAML, see sshmuxd.yml for an example.

{
	// Listening address as given directly to net.Listen.
	"address": ":22",

	// Private key to use for built-in SSH server. Make sure you replace all newlines with \n
	"hostkey": "-----BEGIN EC PRIVATE KEY-----\nMHcCAQEEIJEDt/lHs7jSUNEMbX+Swp6xa8ZiamPFoYsTZgP+We8DoAoGCCqGSM49\nAwEHoUQDQgAEUvK6aRbBnFVsXvpJ9bwUDEI3c/phJAIsjk2dA+YNiFVQq20Xkefl\nFqPJeBriA2EMGkU2AmKGFK45PwRjKI10bA==\n-----END EC PRIVATE KEY-----",

	// Authorized keys to use for authenticating users.
	"users": [
		{
			"publicKey": "AAAAC3NzaC1lZDI1NTE5AAAAIG5AnPTKFnstV0y4n9m4Qo624wEkQKdVKz0HTrsGmecs",
			"name": "me"
		},
		{
			"publicKey": "AAAAC3NzaC1lZDI1NTE5AAAAINg6gWZLnH5gwLeDlw/URtvYgKmlFiiXHmra6oYObfBz",
			"name": "boss"
		},
		{
			"publicKey": "AAAAB3NzaC1yc2EAAAADAQABAAACAQDEmICmR4ZD175AlaWnLrMlHnNTu9MqZplgkj2MZOoX4gODbzlRUcI4MquehcZ3evF7o9GjAMLRVN16fkDBx6YH7tFfQFxsSSfIToXHW0L8k06HW5eoLEo6nZ/mBI7tq98XQ4qWC71n+/O59bGP2mFph1LR4G2m9DApW4I/JVTLqWh0dkQbNV5RrPn9h+nJ0dNUumubUU2uTyd9u6UjCc+Hg9ScqNc3fheNoeS72ihu/33G/O+xwUc0nX+0ngRsUkakGgk5IiU4Lx4xofqMRiKKcQNHajo9tLGKLv11EaMEOdMDVJiTf/JGFyEMofM0tPqJjLXXwtTqgFx/8y5PIiZsuepaouNBWZ3T/Sp8OUZsim67Tllvc7qFZ6zzdMXLD5aKLtqkRgDvP9NuGlHVbq2cdkrCToIjsz5PZo+KvNV6V+Tzy13QKkwkHmabMOMRiR5MH4Jt80lJeOce1Se9e57rHhJ8DmxhEmqNSCIYegrX6hW+8XMRDdmL8OS7US9v7dwpjB+lehDidH2xd3rTzEbR+EeFZJ47L+jSSO+GDYRRB7IffnVf2JAwKCAQu13w8TJwLKBwKUHqcbb/vnofHz6geLweIM03Rbi4e87zdMgeghbM0ys6hAg/ZMwUkpUn89OGtGBE5cmjPYXEdbJcd8EF3LTmEf/POdaJ1qc/ObHbUw==",
			"name": "granny"
		}
	],

	// The list of remote hosts that can be used through this proxy.
	"hosts": [
		{
			// The address of the remote host. This address must include the
			// port.
			"address": "ssh1.example.com:22",

			// The list of users permitted to access this host.
			"users": [ "boss", "me", "granny" ],

			// Whether or not this server can be accessed by anyone,
			// regardless of public key and presence in user list.
			// Defaults to false.
			"noAuth": false
		},
		{
			"address": "public.example.com:22",
			"noAuth": true
		},
		{
			"address": "secret.example.com:22",
			"users": [ "me" ]
		}
	]
}

More info

For more details about this project, see the underlying library: http://github.com/kennylevinsen/sshmux

sshmuxd's People

Contributors

cs-kl avatar erkan-yilmaz avatar kennylevinsen avatar lclc avatar neuhaus avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

sshmuxd's Issues

listen tcp :22: bind: address already in use

I have a problem, please help me check it.

sh-3.2# ./sshmuxd example_conf.json
panic: listen tcp :22: bind: address already in use

goroutine 1 [running]:
main.main()
    /Users/zhangkun/wk/go/src/github.com/joushou/sshmuxd/main.go:184 +0x4b0

goroutine 17 [syscall, locked to thread]:
runtime.goexit()
    /usr/local/go/src/runtime/asm_amd64.s:1721 +0x1
sh-3.2# cat 
.git/              .gitignore         LICENSE            README.md          authkeys           example_conf.json  hostkey            main.go            sshmuxd
sh-3.2# cat example_conf.json 
{
   "address": ":22",
   "hostkey": "hostkey",
   "authkeys": "authkeys",
   "hosts": [
      {
         "address": "10.10.11.14:22",
         "users": [ "root" ]
      },
      {
         "address": "public.example.com:22",
         "noAuth": true
      },
      {
         "address": "secret.example.com:22",
         "users": [ "me" ]
      }
   ]
}

sh-3.2# 

Ignores username when using ssh-ed25519

When I try to login with my ed25519 key I get

unknown user authorized

Since the key is still valid it works, but only for machines that are available for everyone.

Certificate based authentication

I propose to add certificate based authentication in addition to the current simple "public key" -> "username" authentication scheme (authkeys):

  • Add a number of trusted CA certificates to the configuration.
  • Use the subject name in the user certs to directly map to the sshmuxd user name.

This would have the benefit of not having to touch the authkeys when adding new users. Also, certificates have a validity time, which improves security and makes some maintenance processes easier. For example, you can issue a two week certificate to a user that needs access for a shorter task without having to worry about forgetting to remove the user when the job is done.

Bonus point: It could be worthwhile also using the certificate for authorization. For example, in addition to listing the users under a destination host, one could list CA certificates. Users with certificates issued by a CA on the authorized list would have access to the destination. This allows delegating adding users and users could be added without touching the jumphost configuration at all.

Password authentication

Is it possible to run this with password authentication rather than public key auth?

Thanks!

panic: runtime error with unknown user

Built from source on 28th Sept 2015 using go1.4.2 on Linux Mint 17 (Ubuntu derivative), when I connect to sshmuxd as a user that does not have an entry in the authkeys file, and there is a configuration entry that has "noAuth": true set, then there is a runtime error. Ideally this should be handled gracefully to prevent a denial of service.

conf.json:

{
   "address": ":2222",
   "hostkey": "hostkey",
   "authkeys": "authkeys",
   "hosts": [
      {
         "address": "internal.hostname.com:22",
         "noAuth": true
      }
   ]
}

User sees:

$ ssh -p 2222 localhost
Write failed: Broken pipe

Server's stdout:

$ ./sshmuxd conf.json 
2015/09/28 13:16:54 127.0.0.1:55898: unknown user authorized (username: srf)
panic: runtime error: invalid memory address or nil pointer dereference
[signal 0xb code=0x1 addr=0x0 pc=0x4029b6]

goroutine 5 [running]:
main.func·003(0xc20805a380, 0xc20803cd20, 0x24, 0x0, 0x0)
    /home/simon/.gvm/pkgsets/go1.4.2/global/src/github.com/joushou/sshmuxd/main.go:165 +0x216
github.com/joushou/sshmux.(*Server).SessionForward(0xc20803da40, 0xc20805a380, 0x7f2e79b25d50, 0xc20806e0c0, 0xc208052480)
    /home/simon/.gvm/pkgsets/go1.4.2/global/src/github.com/joushou/sshmux/forward.go:163 +0x2c4
github.com/joushou/sshmux.(*Server).HandleConn(0xc20803da40, 0x7f2e79b25500, 0xc208030040)
    /home/simon/.gvm/pkgsets/go1.4.2/global/src/github.com/joushou/sshmux/server.go:122 +0x5f9
created by github.com/joushou/sshmux.(*Server).Serve
    /home/simon/.gvm/pkgsets/go1.4.2/global/src/github.com/joushou/sshmux/server.go:139 +0x9f

goroutine 1 [IO wait]:
net.(*pollDesc).Wait(0xc2080101b0, 0x72, 0x0, 0x0)
    /home/simon/.gvm/gos/go1.4.2/src/net/fd_poll_runtime.go:84 +0x47
net.(*pollDesc).WaitRead(0xc2080101b0, 0x0, 0x0)
    /home/simon/.gvm/gos/go1.4.2/src/net/fd_poll_runtime.go:89 +0x43
net.(*netFD).accept(0xc208010150, 0x0, 0x7f2e79b23a20, 0xc20802a958)
    /home/simon/.gvm/gos/go1.4.2/src/net/fd_unix.go:419 +0x40b
net.(*TCPListener).AcceptTCP(0xc208030038, 0xc208087dd0, 0x0, 0x0)
    /home/simon/.gvm/gos/go1.4.2/src/net/tcpsock_posix.go:234 +0x4e
net.(*TCPListener).Accept(0xc208030038, 0x0, 0x0, 0x0, 0x0)
    /home/simon/.gvm/gos/go1.4.2/src/net/tcpsock_posix.go:244 +0x4c
github.com/joushou/sshmux.(*Server).Serve(0xc20803da40, 0x7f2e79b244b8, 0xc208030038, 0x0, 0x0)
    /home/simon/.gvm/pkgsets/go1.4.2/global/src/github.com/joushou/sshmux/server.go:134 +0x40
main.main()
    /home/simon/.gvm/pkgsets/go1.4.2/global/src/github.com/joushou/sshmuxd/main.go:175 +0x55a

goroutine 6 [IO wait]:
net.(*pollDesc).Wait(0xc208010220, 0x72, 0x0, 0x0)
    /home/simon/.gvm/gos/go1.4.2/src/net/fd_poll_runtime.go:84 +0x47
net.(*pollDesc).WaitRead(0xc208010220, 0x0, 0x0)
    /home/simon/.gvm/gos/go1.4.2/src/net/fd_poll_runtime.go:89 +0x43
net.(*netFD).Read(0xc2080101c0, 0xc20800f000, 0x1000, 0x1000, 0x0, 0x7f2e79b23a20, 0xc20802a920)
    /home/simon/.gvm/gos/go1.4.2/src/net/fd_unix.go:242 +0x40f
net.(*conn).Read(0xc208030040, 0xc20800f000, 0x1000, 0x1000, 0x0, 0x0, 0x0)
    /home/simon/.gvm/gos/go1.4.2/src/net/net.go:121 +0xdc
bufio.(*Reader).fill(0xc208052060)
    /home/simon/.gvm/gos/go1.4.2/src/bufio/bufio.go:97 +0x1ce
bufio.(*Reader).Read(0xc208052060, 0xc2080563a0, 0x5, 0x5, 0xc2080100e0, 0x0, 0x0)
    /home/simon/.gvm/gos/go1.4.2/src/bufio/bufio.go:174 +0x26c
io.ReadAtLeast(0x7f2e79b25710, 0xc208052060, 0xc2080563a0, 0x5, 0x5, 0x5, 0x0, 0x0, 0x0)
    /home/simon/.gvm/gos/go1.4.2/src/io/io.go:298 +0xf1
io.ReadFull(0x7f2e79b25710, 0xc208052060, 0xc2080563a0, 0x5, 0x5, 0x454, 0x0, 0x0)
    /home/simon/.gvm/gos/go1.4.2/src/io/io.go:316 +0x6d
golang.org/x/crypto/ssh.(*streamPacketCipher).readPacket(0xc208056380, 0xc200000008, 0x7f2e79b25710, 0xc208052060, 0x0, 0x0, 0x0, 0x0, 0x0)
    /home/simon/.gvm/pkgsets/go1.4.2/global/src/golang.org/x/crypto/ssh/cipher.go:142 +0xd4
golang.org/x/crypto/ssh.(*connectionState).readPacket(0xc208001200, 0xc208052060, 0x0, 0x0, 0x0, 0x0, 0x0)
    /home/simon/.gvm/pkgsets/go1.4.2/global/src/golang.org/x/crypto/ssh/transport.go:111 +0xe1
golang.org/x/crypto/ssh.(*transport).readPacket(0xc208001200, 0x0, 0x0, 0x0, 0x0, 0x0)
    /home/simon/.gvm/pkgsets/go1.4.2/global/src/golang.org/x/crypto/ssh/transport.go:107 +0x68
golang.org/x/crypto/ssh.(*handshakeTransport).readOnePacket(0xc208001320, 0x0, 0x0, 0x0, 0x0, 0x0)
    /home/simon/.gvm/pkgsets/go1.4.2/global/src/golang.org/x/crypto/ssh/handshake.go:165 +0x101
golang.org/x/crypto/ssh.(*handshakeTransport).readLoop(0xc208001320)
    /home/simon/.gvm/pkgsets/go1.4.2/global/src/golang.org/x/crypto/ssh/handshake.go:145 +0x28
created by golang.org/x/crypto/ssh.newServerTransport
    /home/simon/.gvm/pkgsets/go1.4.2/global/src/golang.org/x/crypto/ssh/handshake.go:120 +0xea

goroutine 7 [chan receive]:
golang.org/x/crypto/ssh.(*handshakeTransport).readPacket(0xc208001320, 0x0, 0x0, 0x0, 0x0, 0x0)
    /home/simon/.gvm/pkgsets/go1.4.2/global/src/golang.org/x/crypto/ssh/handshake.go:136 +0x8a
golang.org/x/crypto/ssh.(*mux).onePacket(0xc2080103f0, 0x0, 0x0)
    /home/simon/.gvm/pkgsets/go1.4.2/global/src/golang.org/x/crypto/ssh/mux.go:224 +0x67
golang.org/x/crypto/ssh.(*mux).loop(0xc2080103f0)
    /home/simon/.gvm/pkgsets/go1.4.2/global/src/golang.org/x/crypto/ssh/mux.go:199 +0x49
created by golang.org/x/crypto/ssh.newMux
    /home/simon/.gvm/pkgsets/go1.4.2/global/src/golang.org/x/crypto/ssh/mux.go:128 +0x19c

goroutine 8 [chan receive]:
golang.org/x/crypto/ssh.DiscardRequests(0xc208052540)
    /home/simon/.gvm/pkgsets/go1.4.2/global/src/golang.org/x/crypto/ssh/connection.go:81 +0x51
created by github.com/joushou/sshmux.(*Server).HandleConn
    /home/simon/.gvm/pkgsets/go1.4.2/global/src/github.com/joushou/sshmux/server.go:113 +0x426

Help sshmuxd not working for me

I have two ssh servers running on my server. Local and a remote forwarded one. Can you kindly let me know what I am doing wrong here.

[Wed Apr 13 6:29:30] [email protected]:~$ sshmuxd example_conf.json
panic: open hostkey: no such file or directory

goroutine 1 [running]:
main.main()
        /home/sandeep/.go/src/github.com/joushou/sshmuxd/main.go:99 +0x160

goroutine 17 [syscall, locked to thread]:
runtime.goexit()
        /usr/lib/go/src/runtime/asm_amd64.s:1696 +0x1
[Wed Apr 13 6:29:36] [email protected]:~$
[Wed Apr 13 6:29:36] [email protected]:~$ cat example_conf.json
{
   "address": ":22222",
   "hostkey": "hostkey",
   "authkeys": "authkeys",
   "hosts": [
      {
         "address": "localhost:2222",
         "users": [ "osmc" ]
      },
      {
         "address": "localhost:22",
         "users": [ "sandeep" ]
      }
   ]
}

Auto reloading of users

Maybe this already exists but is not apparent from the docs, but it would be great if the users were determined dynamically at runtime, that way users can be added without having to restart the daemon.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.