DEV Community

Felipe Arcaro
Felipe Arcaro

Posted on

Accessing my home server from a different continent (the hard way)

TL;DR

I have a home server in Porto, Portugal. When I needed to travel to Brazil for 4 months, I wanted to access my home server from a different network. While there are several tools available for this purpose, I decided to hack my way through it because... why not?

So, I took the following steps:

  • Wrote a script to periodically send me emails informing me if my home server's external IP had changed
  • Configured the required port forwarding on my router
  • Set up SSH keys between my laptop and my home server
  • Implemented some monitoring

My home server's IP

If you've ever tinkered with your home network, you've probably noticed that each device within your house is assigned an internal IP address, allowing them to communicate with each other. However, all these devices share access to the internet through a single IP address provided by your Internet Service Provider (ISP). You can easily find this external IP address on websites like this, but it's important to note that this IP address might change over time.

While there are tools like DuckDNS available to manage dynamic IP changes, I decided to create my own hacky solution – I wrote a script to periodically check if my IP had changed and send me emails using SendGrid.

#!/bin/bash

# Constants
SENDGRID_API_KEY="MY_KEY"
TO_EMAIL="my_email@gmail.com"
FROM_EMAIL="my_email@gmail.com"
CURRENT_IP="my_ip"

# Get the external IP address using an external service
EXTERNAL_IP=$(curl -s http://api.ipify.org)

# Compose the email content
if [ "$EXTERNAL_IP" == "$CURRENT_IP" ]; then
        EMAIL_BODY="Yo, your home server IP address is still the same: $EXTERNAL_IP"
else
        EMAIL_BODY="Yo, it looks like your home server IP address has changed: $EXTERNAL_IP"
fi

EMAIL_SUBJECT="Home Server - Weekly IP check"

# Create a temporary JSON payload file
TMP_JSON=$(mktemp)
echo '{
  "personalizations": [
    {
      "to": [
        {
          "email": "'"$TO_EMAIL"'"
        }
      ],
      "subject": "'"$EMAIL_SUBJECT"'"
    }
  ],
  "from": {
    "email": "'"$FROM_EMAIL"'"
  },
  "content": [
    {
      "type": "text/plain",
      "value": "'"$EMAIL_BODY"'"
    }
  ]
}' > "$TMP_JSON"

# Send email using SendGrid API
curl -X POST "https://api.sendgrid.com/v3/mail/send" \
     -H "Authorization: Bearer $SENDGRID_API_KEY" \
     -H "Content-Type: application/json" \
     --data-binary @"$TMP_JSON"

# Clean up temporary JSON file
rm "$TMP_JSON"

Enter fullscreen mode Exit fullscreen mode

I scheduled the execution of that script using a cronjob (Linux).

Setting up port forwarding

Remember I mentioned all our home devices' traffic exits through a single IP? Well, it works the other way around too – we can use that same IP to access our home devices, with a little help. Most routers come with a firewall that blocks incoming traffic for security reasons.

However, they also offer port forwarding, a feature that allows us to specify rules like "if someone tries to access my IP on port 123, redirect them to this internal IP (specific device) on the same port".

Setting up port forwarding involves accessing our router's settings through a web browser, logging in with your credentials, and configuring the port forwarding rules accordingly. SSH (Secure Shell) connections typically use port 22by default. I decided to use a less common port to avoid attacks targeting the default port.

With port forwarding in place, we are ready to access my home server using SSH with ssh my_user@my_external_ip and entering my password, right? Well, technically yes, but passwords are no fun (and not really secure). That's why it's time to set up SSH keys for extra security.

Configuring SSH keys

Creating keys on my laptop

First, I generated SSH keys on my laptop by running the following command in the terminal:

ssh-keygen -t rsa -b 4096 -C "email@example.com"
Enter fullscreen mode Exit fullscreen mode

By default, both the public and private keys are stored in our home directory within a hidden folder named .ssh. The private key is typically named id_rsa, while the public key has the same name but with a .pub extension (id_rsa.pub).

To view and copy the newly created public key, we can use the command cat ~/.ssh/id_rsa.pub, which will display something like this: ssh-rsa <long-string> email@example.com

Adding the public key to my home server

Next, now on my home server, I log in with the desired user for SSH connections, and create the .ssh folder and authorized_keys file if they don't already exist:

mkdir -p /home/my_user/.ssh && touch /home/my_user/.ssh/authorized_keys
Enter fullscreen mode Exit fullscreen mode

Then, I paste my public key on authorized_keys file using a text editor.

Finally, we ensure that the permissions on the ~/.ssh directory and the authorized_keys file are correctly set for SSH to use them:

chmod 700 /home/my_user/.ssh
chmod 600 /home/my_user/.ssh/authorized_keys
Enter fullscreen mode Exit fullscreen mode

These permissions restrict access to the .ssh directory and its contents to only the user associated with it.

Removing password access

Now that we have properly set up the SSH keys, it's time to remove the password access. For that, we simply change set PasswordAuthentication no inside /etc/ssh/sshd_config and restart the SSH service with sudo service ssh restart.

Monitoring

Although this workflow seems fairly secure, it's always a good idea monitor SSH access. My home server runs on Ubuntu, so I SSH (and other) activity is logged on /var/log/auth.log file.

I created a simple script to check for any changes in that file:

#!/bin/bash

# Constants
SENDGRID_API_KEY="MY_KEY"
TO_EMAIL="my_email@gmail.com"
FROM_EMAIL="my_email@gmail.com"
CURRENT_IP="my_ip"

send_email_notification() {

        # Create a temporary JSON payload file
        TMP_JSON=$(mktemp)
        echo '{
          "personalizations": [
            {
              "to": [
                {
                  "email": "'"$TO_EMAIL"'"
                }
              ],
              "subject": "'"$EMAIL_SUBJECT"'"
            }
          ],
          "from": {
            "email": "'"$FROM_EMAIL"'"
          },
          "content": [
            {
              "type": "text/plain",
              "value": "'"$EMAIL_BODY"'"
            }
          ]
        }' > "$TMP_JSON"

        # Send email using SendGrid API
        curl -X POST "https://api.sendgrid.com/v3/mail/send" \
             -H "Authorization: Bearer $SENDGRID_API_KEY" \
             -H "Content-Type: application/json" \
             --data-binary @"$TMP_JSON"

        # Clean up temporary JSON file
        rm "$TMP_JSON"
}

# Define the critical file to monitor
ssh_logs_file="/var/log/auth.log"

# Store the initial checksums
initial_md5sum=$(md5sum "$ssh_logs_file")
echo "Initial md5sum: $initial_md5sum"

while true; do
    # Calculate the current checksums
    current_md5sum=$(md5sum "$ssh_logs_file")

    # Compare checksums to detect changes
    if [[ "$initial_md5sum" != "$current_md5sum" ]]; then
        send_email_notification
        echo "Oops, it seems like checksum is different, new logs were added to ssh auth.log"
        echo "Initial md5sum: $initial_md5sum"
        echo "Current md5sum: $current_md5sum"
        initial_md5sum="$current_md5sum"
    fi

    # Adjust the sleep interval
    sleep 300
done
Enter fullscreen mode Exit fullscreen mode

Then I ran the script

 chmod +x check-ssh-logs.sh 
 nohup ./check-ssh-logs.sh & 
Enter fullscreen mode Exit fullscreen mode

nohup is a nifty command used to run a process immune to hangups, allowing it to continue running even after logging out

Then I got married, traveled back to Portugal, had to move to a different apartment and haven't turned on my home server ever since.

Top comments (0)