Aptly API over Unix Sockets

ruby

Last week I’ve added a bunch of code to enable use of Aptly’s API over Unix domain sockets.

Aptly is a Debian repository management software we use for KDE neon‘s deb repository. It is one of the more popular software for repo management as it is easy to deploy and features a handy REST API.

One of the more annoying shortcomings of Aptly’s API is the fact that it has no authentication mechanism. This makes using the API from remote hosts a fairly unwieldy affair as you first have to harden the beast.

Your options are basically limited to either run Apache (or some other HTTP server) that proxies Aptly’s API and adds HTTPAuth to it, or you pipe everything through SSH. Both of which are fine solutions, but they hide a fairly awkward security problem.

Aptly would be listening to a port on localhost and the lack of access control means that if any user gets compromised the Aptly instance is effectively compromised as well. It also means that if you do not read the documentation and explicitly set Aptly to listen on localhost only, you end up with a publicly writable Aptly instance. Ugh.

There is a really simple way out though: listening to a Unix domain socket rather than a TCP socket. Unix sockets are pseudo-files and therefore file access control applies. This makes access control easy to manage through the system’s user/group/all permissions of the socket file.

srw-rw----  1 user group    0 Mar  6 14:23 aptly.sock

Making use of this is super easy too!

Socket Listening

Aptly API’s serve invocation now supports a URI scheme for Unix sockets.

aptly api serve -listen="unix://tmp/aptly.sock"

This will (re)create a Unix domain socket at the defined path and listen to it.

Using

Using this you can restrict full access to the API service down to a user level. More fine-grained control still needs a frontend add-on such as an HTTP server with HTTPAuth, but for most purposes a per-user access control should be sufficient and vastly more secure than the previous TCP socket binding.

With OpenSSH’s direct-streamlocal@openssh.com SSH protocol extension you can then even communicate with a remote Aptly instance through a forwarded socket.

To enable myself to use this I’ve also added support for this to the Ruby net/ssh gem and my aptly-api gem.

Using latest master of everything involved we can now serve the API on a socket and then access that remotely via SSH like so:

require 'aptly'
require 'net/ssh'

Thread.new do
  Net::SSH.start('remotehost', 'user') do |ssh|
    ssh.forward.local_socket('/tmp/local.sock', '/home/user/aptly.sock')
    loop { ssh.process(0.1) }
  end
end

sleep 2 # wait for open

Aptly.configure do |c|
  c.uri = URI.parse('unix:///tmp/local.sock')
end
Aptly::Repository.list

Lovely, isn’t it?

Leave a comment