Mostly, when we talk about Tor, we just talk about websites. But what’s about other traffic and tools? What’s about Puppet or Icinga? If you have a Puppet server and you like to hide where it stays and/or which nodes are connected, perhaps you like to serve those services over an onion service.

This article is based on some research how can a Puppet server and an icinga parent be hidden. But only the corresponding traffic should be routed through Tor, the rest of the traffic shouldn’t go through Tor.

Normally Tor provides a local socks proxy, and each application with support for socks proxies and be configured to run over Tor. If your application supports socks proxies, use the socks proxy. If your application supports only http proxies, there are tools, like polipo, in the internet which can translate from http to socks proxy. Puppet can use http proxies and that’s fine. But first of all, on the website of polipo it’s written that polipo isn’t maintained anymore and second, not every application supports proxies.

The goal is to implement a transparent proxy for every kind of (tcp) traffic. This example is built with CentOS 7 and/or 8, it should be possible with every kind of Unix. For the firewall part – it’s essential to configure the firewall correctly – this example uses nftables. The same is also possible with iptables.

Server side

The server side is easy, it’s like every hidden service. Tor runs on the server and creates an onion service which forwards the traffic to a port.
Configure Tor (/etc/tor/torrc):

# Puppet hidden service
HiddenServiceDir /var/lib/tor/puppet
HiddenServicePort 8140

# Icinga hidden service
HiddenServiceDir /var/lib/tor/icinga2
HiddenServicePort 5665

The onion address can be found in the corresponding hostname file, e.g. /var/lib/tor/puppet/hostname. Puppet and Icinga will have different hostnames. As this setup isn’t about how to create onion services, this setup is enough. If you like to dig deeper into onion services, checkout the Tor documentation.

Agent side

The interesting part is the agent side. For a transparent proxy we need multiple parts to work together. First of all, the client will establish a connection to an fqdn, for that we need a dns response for the fqdn. As the fqdn is an onion address, we need a dns server which is aware of onion addresses and will return an ip address. Second of all, the connection will be established directly to an ip address and port, the firewall needs to capture that and redirects it to the Tor service.

DNS

The Tor service can create a dns server (DNSPort) and serve questions. As the Tor service is aware of onion addresses it can return an ip address for an onion address (AutomapHostsOnResolve).

DNSPort 53
AutomapHostsOnResolve 1

After a restart of the Tor service, it will listen on 127.0.0.1:53 (only udp).
If not specified otherwise, the ip range which is used for onion addresses is 127.192.0.0/10 and [FE80::]/10. That’s fine if everything is on one node, if not it’s possible to specify the address range with VirtualAddrNetworkIPv4 and VirtualAddrNetworkIPv6.
The /etc/resolv.conf must be adapted to this and the only nameserver should be 127.0.0.1 as it’s the only one which can serve the onion top level domain too.

Transparent proxy

The Tor service can create a port which serves as a transparent proxy (TransPort).

TransPort 9051

After a restart of the service it will listen on 127.0.0.1:9051 tcp. To redirect all our traffic to the Tor service, some firewall rules are required.

$ cat /etc/sysconfig/nftables.conf
table ip nat {
    chain output {
        type nat hook output priority -100; policy accept;
        meta l4proto tcp ip daddr 127.192.0.0/10 redirect to :9051
    }
}

This will redirect all traffic, which has a destination within the VirtualAddrNetworkIPv4 network to the local Tor service transparent proxy.

Testing

Let’s do some tests first:

$ dig +short @127.0.0.1 kqcbbtuyhuaagan3pbm22knapempzb5ia2wnmdqnhg7rrfwx775cqmad.onion
127.255.217.241
$ dig +short @127.0.0.1 AAAA kqcbbtuyhuaagan3pbm22knapempzb5ia2wnmdqnhg7rrfwx775cqmad.onion
fe96:9715:fe63:392b:bea0:67db:78f8:2434
$ dig +short @127.0.0.1 www.immerda.ch
199.58.80.177

We see, that the dns server respond with an A and AAAA record inside of the network which is specified with VirtualAddrNetworkIPv?. Every other dns query will be answered too. That’s great, what’s about our traffic which should be redirected to the transparent proxy?

$ nmap -sT -p8140 kqcbbtuyhuaagan3pbm22knapempzb5ia2wnmdqnhg7rrfwx775cqmad.onion
Starting Nmap 7.70 ( https://nmap.org ) at 2020-03-22 20:05 UTC
Nmap scan report for kqcbbtuyhuaagan3pbm22knapempzb5ia2wnmdqnhg7rrfwx775cqmad.onion (127.230.205.159)
Host is up (0.0034s latency).
Other addresses for kqcbbtuyhuaagan3pbm22knapempzb5ia2wnmdqnhg7rrfwx775cqmad.onion (not scanned): fea3:1af6:48c2:a7f5:f5ef:bd1d:accc:f623

PORT     STATE SERVICE
8140/tcp open  puppet

Applications

As Tor creates now a transparent proxy, it’s really easy to setup them. Let’s have a look on the two examples Puppet and Icinga.

Puppet

The Puppet server needs a certificates which also includes the onion address as a x509v3 dns alternative name. Have a look at the dns_alt_names configuration in the Puppet documentation.
For an already existing Puppet server, the host certificate has to be removed and regenerated. There is no need to replace the Puppet CA.
On the agent side, we have to specify the onion server address:

$ cat /etc/puppetlabs/puppet/puppet.conf
[main]
    certname = agent.example.com
    server = kqcbbtuyhuaagan3pbm22knapempzb5ia2wnmdqnhg7rrfwx775cqmad.onion
...

Puppet will now use a connection over Tor. On the server side, the certificate of the agent will still include the certname (agent.example.com).

Icinga

This article will just explain some parts of the whole Icinga2 configuration. The whole Icinga configuration is too much and out of scope.
On the monitoring parent node, create the zones and endpoints:

$ cat /etc/icinga2/zones.conf
object Endpoint "monitoring.example.com" {
}
object Zone "monitoring.example.com" {
    endpoints = [ "monitoring.example.com" ]
}

object Endpoint "agent.example.com" {
}
object Zone "agent.example.com" {
    endpoints = [ "agent.example.com" ]
    parent = "monitoring.example.com"
}

On the agent node, create the same zones and endpoints:

$ cat /etc/icinga2/zones.conf
object Endpoint "monitoring.example.com" {
    host = "kqcbbtuyhuaagan3pbm22knapempzb5ia2wnmdqnhg7rrfwx775cqmad.onion"
    port = 5665
}
object Zone "monitoring.example.com" {
    endpoints = [ "monitoring.example.com" ]
}


object Endpoint "agent.example.com" {
}
object Zone "agent.example.com" {
    endpoints = [ "agent.example.com" ]
    parent = "monitoring.example.com"
}

All done. Icinga will now communicate over Tor.

Conclusion

It’s relative simple to create a transparent Tor proxy, but it’s not really well documented in the internet. There were blog articles which described parts of it, and every configuration option is well documented in the man page. A negative point is, that the dns server of the Tor service will not serve every dns type. Perhaps it would be better to create a onion router which serves within a network as a transparent proxy and configure your main dns server to stub resolv onion addresses on this onion router.