SSH point-to-point tunnel
Objective
When doing a internal network assessment, it may be required to tunnel traffic trough a device deployed on premise. This device can be a virtual machine, a raspberry pi, etc.
A common way to go about this is to have the device connect back to a server under your control and expose its SSH port. Then use DynamicForward to create a SOCKS proxy.
The downside to this is that each tool/command needs to be configured to use the proxy. Also, workarounds to tunnel UDP traffic such as DNS need to be configured.
Using a point-to-point (PtP) tunnel fixes all the SOCKS proxy issues. Traffic is sent trough the tunnel via routes (L3) so it handles TCP and UDP natively.
Configuration
Local
ssh_config
The following is an host block example with all the options required.
Host ptp
Hostname $hostname
User $user
Port $port
IdentityFile $ssh_key
SessionType none
Tunnel point-to-point
TunnelDevice 1337:1337
ExitOnForwardFailure yessystemd-networkd NetDev
By creating the TUN device ahead of time, we can assign it the right permissions to avoid using root privileges. The permissions are managed under the [Tun] section. Configure the relevant values for the user that will establish the SSH connection.
# /etc/systemd/network/ssh_ptp.netdev
[NetDev]
Name=tun1337
Kind=tun
[Tun]
Group=$group
User=$usersystemd-networkd Network
Time to configure the network interface. The configuration will contain at the minimum the required information to reach the peer on the other side of the tunnel. At most, the configuration may contain reachable network routes, and remote DNS resolution.
# /etc/systemd/network/ssh_ptp.network
[Match]
Name=tun1337
[Address]
Address=192.0.2.253/30
Peer=192.0.2.254/32
[Network]
# Disable IPv6
LinkLocalAddressing=ipv4
# Remote authoritative DNS for local domain
#DNS=x.x.x.x
#Remote DNS zone(s) - space separated
#Domains=~example.com
# This block adds reachable networks to the local routing table
# The routes are added when the interface is operational, and are removed when it is not.
# Multiple [Route] sections can be added to reach more networks
#[Route]
#Gateway=192.0.2.254
#Destination=x.x.x.x/xThe final step is to reload systemd-networkd.
$ sudo networkctl reloadRemote
sshd_config
Edit the SSH server configuration to enable Tunneling and reload the service
# /etc/ssh/sshd_config
PermitTunnel yes$ sudo systemctl reload sshsystemd-networkd NetDev
By creating the TUN device ahead of time, we can assign it the right permissions to avoid using root privileges. The permissions are managed under the [Tun] section. Configure the relevant values for the user that will establish the SSH connection.
This will be the user account used to connect to the SSH server.
# /etc/systemd/network/ssh_ptp.netdev
[NetDev]
Name=tun1337
Kind=tun
[Tun]
Group=$group
User=$usersystemd-networkd Network
Time to configure the network interface. The configuration will contain at the minimum the required information to reach the peer on the other side of the tunnel.
# /etc/systemd/network/ssh_ptp.network
[Match]
Name=tun1337
[Address]
Address=192.0.2.254/30
Peer=192.0.2.253/32
[Network]
# Disable IPv6
# Can be set to 'no' if older systemd version
LinkLocalAddressing=ipv4The final step is to reload systemd-networkd.
$ sudo networkctl reloadConfigure NAT/Masquerade
Time to configure NAT so the incoming traffic from the tunnel will exit using the remote LAN interface IP.
$ sudo iptables --policy FORWARD ACCEPT
$ sudo iptables -t nat -A POSTROUTING -s 192.0.2.253 ! -d 192.0.2.254 -o REMOTE_OUTGOING_INTERFACE -j MASQUERADEEnable IP Forwarding
Configure the system to forward packets
$ sudo sysctl -w net.ipv4.ip_forward=1Diagnostic
Once both ends are configured and the required services are reloaded (SSH and systemd-networkd), it is time to validate that all is in working order. Of course, you may simply connect to the SSH host and go about it.
Check that the TUN devices are created (local and remote)
ip -brief link show type tunCheck that each the tunnel peers can reach each other
Local
$ ping 192.0.2.254Remote
$ ping 192.0.2.253Check that the outgoing traffic is using the remote LAN IP
The idea is to generate traffic and listen for it using tcpdump. The following example uses ICMP, but any protocol will do.
Remote
$ sudo tcpdump -i tun1337 -f 'icmp'Local
$ ping REMOTE_HOSTCheck that the route blocks in systemd-networkd were added
$ ip route listShould the routes not be there, check the logs with journalctl -eu systemd-networkd.
List the NAT entries
$ sudo iptables -t nat -L