The second part is live: Writing a replacement to OpenSSH using Go (2/2)
SSH is a well-known protocol to access remote servers and OpenSSH is the most common implementation. This article will explain how to build a SSH server and client using Go.
- Why OpenSSH Server is not sufficient in a distributed environment
- What are the available tools and libraries in Go to use the SSH protocol
- A few implementation examples
Git and SSH
In a Platform as a Service such as Appsdeck, the standard way to deploy an application is to use
git push. Underneath,
git is simply using the
ssh command. The two lines below explain the link between them:
# With appsdeck remote: firstname.lastname@example.org:myproject.git ~/myproject $ git push appsdeck master # Git will execute: ~/myproject $ ssh email@example.com git-receive-pack myproject.git
Actually, it is possible to tell
git to use another SSH client, by setting the environment variable
To authenticate a user to a remote server using OpenSSH Server, the user’s public SSH key has to be appended in the
~/.ssh/authorized_keys file of the target user. If OpenSSH finds the public key in this file, the authentication is considered successful. By default, it would execute the shell of the user, defined by its account.
It is possible to execute a custom command by prepending it to the public SSH key in the
command="ssh-handler" ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAA[...]2bF26cl
In this case, instead of running the shell of the user, it will run the command “ssh-handler”. Our first prototype was using this feature to authenticate and authorize our users. However, as soon as you want to build a real distributed infrastructure, it’s not sufficient anymore. Indeed, with this method, it means that several hosts have to keep their
authorized_keys file containing all the public keys of our users in sync. That’s two problems in one : we had to find a synchronization mechanism, and, sensitive data are spread out on several machines.
That’s why we decided it was time to build something more robust and scalable by writing our own custom SSH server, which would be able to use a custom backend to identify our users and then to forward the SSH connection to another host which will actually execute the GIT operation.
SSH in the Go world
We decided to write this custom SSH server with Go for different reasons. Except the fact that we had experience with the language, this official package (http://godoc.org/golang.org/x/crypto/ssh) implements everything we needed to build what we were looking for, while keeping it simple.
At Appsdeck, we are looking to respect the KISS principle. If a software is simple, it is easier to conserve readability, modularity and easy maintenance. By the way, that’s also why Go has become one of our core languages.
The SSH RFCs
While the Go package implements SSH connections setup (that includes transport and crypto), you still have to understand and to use SSH applicative protocol to build your software.
To achieve this, it is important to get how SSH is working, the following RFCs define the protocol:
- Architecture: https://tools.ietf.org/html/rfc4251
- Authentication protocol: https://tools.ietf.org/html/rfc4252
- Transport protocol: https://tools.ietf.org/html/rfc4253
- Channel management and connection protocol: https://tools.ietf.org/html/rfc4254 (probably the most useful when building your server)
Anytime you need to get explanations about a method or a constant of the Go package, refering to the RFCs is pretty straightforward with a simple text search.
(RFCs 4255 and 4256 also concern SSH but are less useful in this scope)
1. Basic server
The following code example defines a simple SSH server: https://github.com/Scalingo/go-ssh-examples/blob/master/server.go
This server only displays the kind of SSH key used for authentication (if any), accepts it, then closes the connection.
└> go run server.go 2014/12/05 15:41:27 [::1]:46428 authenticate with ssh-rsa 2014/12/05 15:41:27 Connection from [::1]:46428
└> ssh localhost -p 2222 Connection to localhost closed by remote host. Connection to localhost closed.
As expected, our Go server succeeds to speak with OpenSSH, but then the connection is closed instantly.
2. Basic client
go run client.go <user> <server:port> <command>
This client has only one job, it connects to a server, creates a sessions, executes a command, then prints the output and disconnects.
└> go run client.go foobar example.com:22 'ls /' Password: ********* bin boot conf.d dev etc home initrd.img lib[...]
This article introduces our problematic and how to use the SSH protocol with Go. In the next article we’ll explain how we use this library to solve our real problems (Authentication, Authorization, Connection Proxying).
To be continued…
… __Ready? Second part is here: Writing a replacement to OpenSSH using Go (2/2)
- Package documentation: http://godoc.org/golang.org/x/crypto/ssh
- Code snippets: https://github.com/Scalingo/go-ssh-examples
This article was the 2nd post of the serie #FridayTechnical, see you next time!
Gopher image by Renee French (Creative Commons Attributions 3.0) Puffy - OpenSSH logo
— Léo Unbekandt, CTO @ Appsdeck