/posts /about

How I do connect home from remote?

August 1, 2019.

Although not often, I sometimes need to connect to my machine at home from work. In this post, I am going to tell you how I do that through SSH.

Trinity from Matrix using SSH

To create a SSH connection, I first need to have the address of my machine at home. This is, of course, made of TCP/IP address and port. The problem is that ISPs, in general, work with dynamic IPs. Even if I know my current IP address, this could change at anytime. ISP allocates me, as its customer, an address which is leased to me for some time only. On the other hand, I could demand to have a static IP address, however this comes with a cost. To give a figure, as of today, my ISP charges TRY24.9 per month for such a service. In my opinion, it’s not only the cost, though. To me, it less secure to expose my home network behind a fixed address.

In order to create a remote connection home, the first thing to do is to obtain my address, somehow. If only someone monitored my address constantly at home and let me know every time it changes…

Let’s forget about the IP addresses for a moment. Nobody would memorize a group of numbers that constantly changes. After all, we have domain name servers for this purpose, right? It’s just nothing but an address record that resolves to an IP address.

Some DNS hosting services let you update DNS records programmatically. Instead of going to DNS panel and enter address records manually, you can achieve the same with some API requests to the DNS provider1. This is called Dynamic DNS (or DDNS).

If we use a domain name, we wouldn’t care what the IP address is. A DNS record would abstract the ever-changing IP address.

We have our first milestone right now.

Milestone 1

Here is an implementation in Python. See github.com/karakays/ddns for source code.

53
54
55
56
57
58
59
60
61
62
def main():
    ch_addr = None
    try:
        with io.open('/tmp/ddns_addr', 'r') as f:
            ch_addr = f.readline()
    except IOError:
        pass
    curr_addr = resolve_addr()
    if curr_addr != ch_addr:
        update_dns(curr_addr)

What this code snippet basically does is it first resolves current public IP address with resolv_addr(). In case it has changed since the last time, it updates DNS records using Cloudflare API and stores the IP address in somewhere local to compare it for the next time. That’s it.

To schedule the service, I will let systemd timers to manage it. ddns.service unit and its matching timer are shown next. [Service] section in the unit file contains environmental variables that are within the scope of this service only.

[Unit]
Description=Update DNS address record of the host

[Service]
Type=oneshot
Environment=CF_API_KEY=changeit
Environment=CF_EMAIL=changeit
Environment=CF_ZONE=changeit
Environment=CF_DOMAIN=changeit
ExecStart=/usr/local/bin/ddns

The timer file specifies to run the service every 5 minutes which is expressed with OnCalendar directive. After directive makes sure it’s activated after network services start up and the host gets an IP address.

[Unit]
Description=Run ddns.service every 5 minutes
After=network.target

[Timer]
OnCalendar=*:*:0/5
AccuracySec=1min
Persistent=true

[Install]
WantedBy=timers.target

To install the service, put the files ddns.service and ddns.timer in /etc/systemd/system path, activate it once and start manually. From there on, ddns.service is managed by systemd - started on every boot and scheduled for every 5 minutes.

$ sudo move ddns.timer ddns.service /etc/systemd/system/
$ sudo systemctl enable ddns.timer
$ sudo systemctl start ddns.timer

By now, we can resolve our IP address by the domain name, but we still can’t reach our target machine in local network. Any request from Internet will hit the gateway, but my target host is in the LAN so its address needs to be translated into the local network. Here goes our second milestone.

Milestone 2

Home network

This is done in the router panel configuration. Port forwarding is redirecting the incoming traffic from the internet to a target host. The exact configuration may change based on your device vendor, but to set up a forwarding rule, you need at least following details -

Since internal IP address and port is fixed in the mappings here, the traffic gets redirected always to the same local address. That means we need to make sure our target host always gets the same IP address assigned from the server. To do that, I edit my network configuration details in a static manner. First, I stop NetworkManager.

$ sudo systemctl stop network-manager
As my target machine is a Debian-based distro, the NIC file is located at /etc/network/interfaces. A sample configuration file is shown next. wlp3s0 is my wireless network interface.
allow-hotplug wlp3s0
iface wlp3s0 inet static
    address <address>
    netmask <netmask>
    network <network>
    broadcast <broadcast>
    gateway <gateway>
    wpa-ssid <network-name>
    wpa-psk <wpa-pw>

To apply the configuration, save the file and reboot the machine.

Some security considerations

In the other end of the connection, SSH server is listening for commands from the Internet. This requires to be a bit cautious. Our target host on port 22 is now exposed to the entire Internet. That means anyone that acquires enough details can connect to your home network, just the same way as you do. This leads to a serious vulnerability. In order to mitigate the risks, we need to take some security measures in the SSH server. The exact details might be in another post but to name a few, we need to -

And that’s it. This is how I connect home from anywhere.

$ ssh mydomain.com

  1. In case your current DNS hosting provider do not support DDNS, you may want to migrate to another one with such capability. I had my domain records managed in a DNS provider other than my domain name registrar. I updated NS records in the DNS provider that I was migrating from, however, this was a mistake. In order to migrate properly, you should update authoritative name server records in your registrar. [return]
Send feedback to me @karakays_.

← Taking the Linux Foundation exam