SSH, the home network is torifying

I like to be able to access my home network remotely. My connection to the Internet is actually a rebroadcasting of various local WiFi networks, which presents a challenge for remote ssh access, since the IP address is dynamically assigned and changes often. My home network is served by a wireless AP that acts as a router, handing out local IP addresses via DHCP. Also, I like to be able to access the web interface for the upstream wireless access point and change networks remotely, while having the option of using a different internet connection on my laptop. While there are ways to do this with dynamic port forwarding, this is not all that easy considering that I do not have access to the upstream router. There are also security concerns with having the home network so exposed and advertised to the outside world. I have chosen to use a Tor hidden onion service as a way to provide a consistent, secured pathway, which I don’t have to reconfigure regardless of what network the home access point is on or where I am with my laptop.

This approach comes with several advantages. First of all, the Tor network use multiple layers of encryption by default. “The client negotiates a separate set of encryption keys for each hop along the circuit to ensure that each hop can’t trace these connections as they pass through.” This means that each node in the network is limited in the information that it knows, only being able to receive data from on relay and pass it on to the next. With the exception of the hop between the exit node and the open internet, all traffic remains encrypted throughout the Tor network. Tor onion services, on the other hand, provide full end-to-end encryption, using through a Tor node that acts as a rendezvous point, which helps to guard against traffic analysis. By building a full Tor circuit between the rendezvous point and the node serving a hidden onion service, the location anonymity of the onion server is preserved.


preparation:

I like to use separate ssh keys for different services, but it’s arguably not necessary from a security standpoint. As far as privacy is concerned, however, the use of the same ssh key to access multiple servers reveals that the same user/entity has access, which is something to be aware of. Either way, newer versions of OpenSSH allow us to use fast elliptic curve cryptography, that provides the security of RSA. We need to generate an ssh key on the client machine such as:

user@client$ ssh-keygen -a 100 -t ed25519 -C <comment> -f ~/.ssh/<private-key-name> 

This specifies the number of rounds (100), that we want the key derivation function to be run to slow down brute-forcing attempts on the password of our private key should it be stolen, or accidentally committed to GitHub. Also, we are specifying that we want to use Ed25519 keys (supported as of OpenSSH 6.5), which are performant and secure. Also, we have the option of giving the key a comment so that we can more easily identify it in the authorized_keys file on a remote host.

Now we need to get the public key of the newly-generated ssh key onto the remote host. We can use a function like ssh-copy-id to upload it to the remote server. Also, one benefit of using ed25519 instead of RSA keys is that the public key is short enough that we can also type it directly into the authorized_keys file without too much trouble.

user@client$ cat ~/.ssh/<private-key-name>.pub
ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAI<some-random-44-char-string> <comment>

If it is not possible yet to log on to the remote server, copy this key using sneakernet to the box and then:

root@router# cd "$HOME" && mkdir .ssh 
root@router# cat /path/to/<private-key-name>.pub >> ~/.ssh/authorized_keys

It is critical that the file system permissions on both the ~/.ssh directory and the authorized_keys file are correct:

root@router# chmod 0700 .ssh && chmod 0600 ~/.ssh/authorized_keys

server configuration:

On the box you want to connect to from the outside (the router in this case), let’s harden the ssh server configuration using Mozilla’s best practices. Because we have root, update everything and use the Modern configuration. Since logging in is only allowed via public key, (password authentication and challenge-response authentication are explicitly disabled), we are allowing root login (This could also be set to ProhibitPassword or WithoutPassword, but that is redundant in this context). This is a matter of preference and may be a security trade-off, however the assumption here is that the key can not be realistically brute-forced, and since we are putting the ssh connection behind a stealth onion service, there is a level of authentication over the Tor network that has to happen before even attempting to brute-force the OpenSSH login.

/etc/ssh/sshd_config

# Password based logins are disabled - only public key based logins are allowed.
AuthenticationMethods publickey
PermitRootLogin Yes

# Use kernel sandbox mechanisms where possible in unprivileged processes
# Systrace on OpenBSD, Seccomp on Linux, seatbelt on MacOSX/Darwin, rlimit elsewhere.
UsePrivilegeSeparation sandbox

# Enable PAM.
PasswordAuthentication no
ChallengeResponseAuthentication no
UsePAM yes

Next, we must configure Tor to allow access to our service on the correct port. First, create a directory for the service in the Tor user’s home directory (often located at /var/lib/tor) and give it the correct permissions:

root@router# install -o tor -g tor -m 0700 -d /var/lib/tor/hidden_ssh_service

Now, edit the torrc on the server (router) to let tor know where to create the files for our ssh service:

/etc/tor/torrc

## SSH as a hidden service ("FREE" NAT Punching)
HiddenServiceDir /var/lib/tor/hidden_ssh_service/
HiddenServicePort 22 127.0.0.1:22
#HiddenServiceAuthorizeClient auth-type client-name,client-name,...
HiddenServiceAuthorizeClient stealth <client-name>

After making these changes, make sure to enable and reload the tor daemon for them to take effect.

If using OpenRC (used by {gen,fun}too and alpine:

root@router# rc-update add tor default &&
             /etc/init.d/tor restart

If using systemd:

root@router# systemctl enable tor.service &&
             systemctl restart tor.service

client configuration:

Since we will be using this connection for administration, lets create some configuration options that allow us to connect easily to the router. First, we will increase the default security options of our ssh client:

/etc/ssh/ssh_config

# Mozilla Modern OpenSSH client configuration:
# https://wiki.mozilla.org/Security/Guidelines/OpenSSH#Modern

# Ensure KnownHosts are unreadable if leaked - it is otherwise easier to know which hosts your keys have access to.
HashKnownHosts yes

# Host keys the client accepts - order here is honored by OpenSSH
HostKeyAlgorithms ssh-ed25519-cert-v01@openssh.com,sh-rsa-cert-v01@openssh.com,ssh-ed25519,ssh-rsa,ecdsa-sha2-nistp521-cert-v01@openssh.com,ecdsa-sha2-nistp384-cert-v01@openssh.com,ecdsa-sha2-nistp256-cert-v01@openssh.com,ecdsa-sha2-nistp521,ecdsa-sha2-nistp384,ecdsa-sha2-nistp256

KexAlgorithms curve25519-sha256@libssh.org,ecdh-sha2-nistp521,ecdh-sha2-nistp384,ecdh-sha2-nistp256,diffie-hellman-group-exchange-sha256

MACs hmac-sha2-512-etm@openssh.com,hmac-sha2-256-etm@openssh.com,umac-128-etm@openssh.com,hmac-sha2-512,hmac-sha2-256,umac-128@openssh.com

Ciphers chacha20-poly1305@openssh.com,aes256-gcm@openssh.com,aes128-gcm@openssh.com,aes256-ctr,aes192-ctr,aes128-ctr

Now lets add the proper aliases in our local .ssh/config file, so that we can connect to the box without a whole bunch of typing:

~/.ssh/config

# Global defaults
Host *
    # Only offer identity specified by 'IdentityFile'
    IdentitiesOnly yes
    # Passwords are dumb
    PreferredAuthentications publickey

# the 'Host' field is an arbitrary name or alias '*' above is a wildcard
Host <cool_router_alias>
    # This is the router on the home network: substitute the local (NAT)
    # IP Address for the Hostname often in the form: {192.168,10.0}.{0,1}.1
    Hostname     X.X.X.X
    Port         22
    User         root
    # Authenticate using the following ssh key
    IdentityFile ~/.ssh/id_ed25519

Host <cool_router_alias>-tor 
    #This is the router accessible via a Tor hidden service
    HostName <onion-address>.onion
    Port 22
    User root
    IdentityFile ~/.ssh/id_ed25519
    # Only use one of the following 'ProxyCommand' lines:
    # openbsd-netcat
    ProxyCommand nc -X 5 -x 127.0.0.1:9050 %h %p
    # ncat (from nmap) (commented out)
    ProxyCommand ncat --proxy-type socks5 --proxy 127.0.0.1:9050 %h %p

Configuring the client (laptop) requires the appropriate data to be placed in the local torrc. We can get the required info for the client from the files in /var/lib/tor/hidden_ssh_service/. The values for ‘HidServAuth’ can be found in the hostname file.

/etc/tor/torrc

User tor
PIDFile /var/run/tor/tor.pid
Log notice syslog
DataDirectory /var/lib/tor/data

## For arm/nyx connection
ControlPort 9051
CookieAuthentication 1

############################################################################
## Connect via <bridge relay name>
## This is only necessary if a direct connection to tor is not possible
## or to obfuscate the fact that you are connecting to tor at all.
## For more info, see: https://www.torproject.org/docs/bridges and 
## https://bridges.torproject.org/ for a list of known bridges.
## Otherwise, comment out the following 3 lines or change 'UseBridges' to 0
UseBridges 1
Bridge obfs4 <ip address>:<port> [fingerprint] cert=<sanitized> iat-mode=0
ClientTransportPlugin obfs4 exec /usr/bin/obfs4proxy
############################################################################

## find this info in '/var/lib/tor/hidden_ssh_service/hostname' or similar
## SSH access to $router  # client: <client-name>
HidServAuth <onion-address>.onion <auth-cookie>

## SSH access to $other_box  # client: <client-name>
HidServAuth <onion-address>.onion <auth-cookie>

## Allow arm / nyx to see connections
DisableDebuggerAttachment 0

## Exclude nodes based on GeoIP FiveEyes included
#ExcludeNodes {ro},{gb},{us},{de},{au},{nz},{ru},{cn},{sy},{il},{fr},{se}
#ExcludeExitNodes {ro},{gb},{us},{de},{au},{nz},{ru},{cn},{sy},{il},{fr},{se}

Once this is done, restart tor on the client:

If using OpenRC (used by {gen,fun}too and alpine:

root@router# rc-update add tor default &&
             /etc/init.d/tor restart

If using systemd:

root@router# systemctl enable tor.service &&
             systemctl restart tor.service

Once everything is setup, try connecting to your box via SSH over tor:

user@client$ ssh <cool_router_alias>-tor

If it works, you should be presented with something like:

Last login: <Day> <Month> <Date> HH:MM:SS YYYY from <client ip address>
root@router:~# 

Congratulations, your home network is now accessible on the dark web!


bonus

With tor running on the client, it is also possible to browse the internet from the IP of the remote host using an ssh tunnel with dynamic port forwarding with a socks5 proxy:

On the client:

/usr/bin/ssh -v -N -D 9999 <hostname>-tor

The following is an excerpt of the relevant man page

SSH(1)                         Gentoo General Commands Manual                          SSH(1)

    -D [bind_address:]port
            Specifies a local “dynamic” application-level port forwarding.  This works by
            allocating a socket to listen to port on the local side, optionally bound to the
            specified bind_address.  Whenever a connection is made to this port, the connec‐
            tion is forwarded over the secure channel, and the application protocol is then
            used to determine where to connect to from the remote machine.  Currently the
            SOCKS4 and SOCKS5 protocols are supported, and ssh will act as a SOCKS server.
            Only root can forward privileged ports.  Dynamic port forwardings can also be
            specified in the configuration file.

    -N      Do not execute a remote command.  This is useful for just forwarding ports.

    -v      Verbose mode.  Causes ssh to print debugging messages about its progress.  This
            is helpful in debugging connection, authentication, and configuration problems.
            Multiple -v options increase the verbosity.  The maximum is 3.

AUTHORS
    OpenSSH is a derivative of the original and free ssh 1.2.12 release by Tatu Ylonen.
    Aaron Campbell, Bob Beck, Markus Friedl, Niels Provos, Theo de Raadt and Dug Song removed
    many bugs, re-added newer features and created OpenSSH.  Markus Friedl contributed the
    support for SSH protocol versions 1.5 and 2.0.

Gentoo/linux                          February 23, 2018                          Gentoo/linux

Now connect via Chrom{e,ium}:

chromium --proxy-server=socks5://127.0.0.1:9999

Alternatively, here is a screenshot of changing the proxy settings in Firefox:

firefox_proxy

Now navigate your browser to DNS leak test and see where you are. You can also check for IPv6 leaks. Assuming everything is working, you internet traffic is now originating from your home connection. Tor does add latency, however, so the price for being home away from home using this method, will probably be pretty noticeable. Also, any website you visit outside of the tor network will be tied to you, which may or may not be a problem. If you are behind a firewall, however, you may benefit from the less restrictive policies of your home network. This is just a starting point, but if what you need is a VPN, there are better solutions.