Administering servers over Tor using Ansible.

Dec 02 2017

Difficulty rating: 8.  Highly specific use case, highly specific setup, assumes that you know what these tools are already.

Let's assume that you have a couple of servers that you can SSH into over Tor as hidden services.

Let's assume that your management workstation has SSH, the Tor Browser Bundle and Ansible installed.  Ansible does all over its work over an SSH connection, so there's no agent to install on any of your servers.

Let's assume that you only use SSH public key authentication to log into those servers.  Password authentication is disabled with the directive PasswordAuthentication no in the /etc/ssh/sshd_config file.

Let's assume that you have sudo installed on all of those servers, and at least one account can use sudo without needing to supply a password.  Kind of dodgy, kind of risky, mitigated by only being able to log in with the matching public key.  That seems to be the devopsy way to do stuff these days.

Problem: How to use Ansible to log into and run commands on those servers over the Tor network?

Ansible has you specify all of the servers you plan on administering with it in the file /etc/ansible/hosts by default.  A block of Tor servers would be specified like this in the file in the user@host format:

[tor]
alice@abcdefghijklmnop.onion
bob@bcdefghijklmnopq.onion
charlie@cdefghijklmnopqr.onion
dan@defghijklmnopqrs.onion
erin@efghijklmnopqrst.onion

Configuration variables that only apply to a particular set of machines in /etc/ansible/hosts are set up by taking the label for the set of machines and appending the string ":vars" to it.  Our set of Tor servers will have the following option block added to the /etc/ansible/hosts file:

[tor:vars]
ansible_ssh_common_args: '-o "ProxyCommand socat STDIO
    SOCKS4A:127.0.0.1:%h:%p,socksport=9150"'

That should be all on one line, I broke it in half to better fit on the page.

The variable "ansible_ssh_common_args" is a list of options that you could pass to the ssh utility on the command line if you wanted to.  By putting them into a configuration option, Ansible applies them to all SSH sessions created to the matching block of servers.  What it means is basically this:

  • Before trying to connect to a server, run the command socat.
  • When you run socat, run all outbound network traffic through socat's stdin, and accept all inbound network traffic from socat's stdout.  This generalizes to the word STDIO.
  • Tell socat to use the network proxy protocol SOCKS 4a.
  • The network proxy server is listening on the IP address 127.0.0.1.
  • %h - The hostname to connect to.
  • %p - The post on the host to connect to
  • The port the SOCKS 4a server is listening on is 9150/tcp (opened by default by the Tor Browser Bundle).

I recommend that you bump up the connection timeout to 60 seconds because it takes time to set up a Tor hidden service connection, let alone a couple of them.  Edit the file /etc/ansible/ansible.cfg and make the following change:

timeout = 60

You will need to install socat on your management workstation, also.  Most distributions of Linux offer it as a package for installation.

Now use Ansible to ping your tor servers.  It'll take a while so be patient.

ansible tor -m ping

If everything worked and you remembered to start the TBB on your workstation, you should get the following output:

alice@abcdefghijklmnop.onion | SUCCESS => {
    "changed": false, 
    "ping": "pong"
}
bob@bcdefghijklmnopq.onion | SUCCESS => {
    "changed": false, 
    "ping": "pong"
}
charlie@cdefghijklmnopqr.onion | UNREACHABLE! => {
    "changed": false, 
    "msg": "Failed to connect to the host via ssh: Connection timed out during banner exchange\r\n", 
    "unreachable": true
}
dan@defghijklmnopqrs.onion | SUCCESS => {
    "changed": false, 
    "ping": "pong"
}
erin@efghijklmnopqrst.onion | SUCCESS => {
    "changed": false, 
    "ping": "pong"
}

Oops.  Looks like cdefghijklmnopqr.onion is offline.  Better call Charlie and have him reboot the server.