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"
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:
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:
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:
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:
#!/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
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.
Top comments (0)