Accessing my home server from a different continent (the hard way)
Tags: ssh, homeserver, bash
Published: 2024-03-30 20:54:27

## 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](https://whatismyipaddress.com/), but it's important to note that this IP address might change over time.

While there are tools like [DuckDNS](https://www.duckdns.org/) 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. 

```bash
#!/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"

```

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 `22`by 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:

```bash
ssh-keygen -t rsa -b 4096 -C "email@example.com"
```

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:

```bash
mkdir -p /home/my_user/.ssh && touch /home/my_user/.ssh/authorized_keys
```

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:

```bash
chmod 700 /home/my_user/.ssh
chmod 600 /home/my_user/.ssh/authorized_keys
```

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:

```bash
#!/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
```

Then I ran the script

```bash
 chmod +x check-ssh-logs.sh 
 nohup ./check-ssh-logs.sh & 
```

> `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.