DEV Community

Cover image for Building a Secure SFTP Server on a Linode Public Subnet
JOOJO DONTOH
JOOJO DONTOH

Posted on

Building a Secure SFTP Server on a Linode Public Subnet

In the previous post, I walked through setting up a bare-ish-metal cloud environment with Linode, partitioning public and private subnets, and wiring up your own proxies, and firewalls — without handing everything off to someone else. If you haven't read it, I suggest you do!

Today, I intend to go one level deeper:
For basic learning purposes, let’s build a secure SFTP server from scratch using the node in our public subnet.

Why should you do this you ask?
Because file transfer is a foundational primitive in ops, and there’s no reason to let that knowledge slip through the cracks.

Why Build Your Own SFTP Server?

  • You don’t need to rely on a SaaS or managed service (lots of manual ops btw)
  • You control access, retention, and isolation
  • You understand how file access and security actually work under the hood
  • You can build automations and workflows around it

This is especially useful when:

  • You’re collaborating with partners who need to send you files securely
  • You want to ship logs, reports, or ETL inputs into your infra
  • You’re learning how SSH, chroot jails, and Linux permissions actually work

Prerequisites

  • A provisioned Linode instance in your public subnet
  • A public IP address and port 22 open to trusted IPs
  • Basic Linux CLI knowledge

I handled the first 2 points in this article
We'll be using Ubuntu 22.04 LTS, but this works on most distros with openssh-server.

Step-by-Step Setup

1. Install OpenSSH

Make sure SSH is installed and running:

sudo apt update && sudo apt install openssh-server -y
Enter fullscreen mode Exit fullscreen mode

2. Create an SFTP-Only User

sudo groupadd sftpusers

sudo useradd -m -G sftpusers -s /sbin/nologin sftpuser1
sudo passwd sftpuser1
Enter fullscreen mode Exit fullscreen mode

This prevents shell access and groups users logically.

3. Create a Secure Directory Structure

OpenSSH’s ChrootDirectory requires that the parent dir is owned by root and not writable.

sudo mkdir -p /sftp/sftpuser1/upload
sudo chown root:root /sftp/sftpuser1
sudo chmod 755 /sftp/sftpuser1

sudo chown sftpuser1:sftpusers /sftp/sftpuser1/upload
Enter fullscreen mode Exit fullscreen mode

This creates a writable /upload directory while keeping the jail secure.

4. Configure sshd_config for SFTP Jail

Append this block to the bottom of /etc/ssh/sshd_config (sudo nano /etc/ssh/sshd_config to open):

Match Group sftpusers
  ChrootDirectory /sftp/%u
  ForceCommand internal-sftp
  X11Forwarding no
  AllowTcpForwarding no
Enter fullscreen mode Exit fullscreen mode

Then reload SSH:

sudo systemctl restart ssh
Enter fullscreen mode Exit fullscreen mode

5. Use SSH Key Authentication

On your local machine:

ssh-keygen -t rsa -b 4096 -f ~/.ssh/sftpuser1_key
Enter fullscreen mode Exit fullscreen mode

On your server:

sudo mkdir -p /home/sftpuser1/.ssh
sudo nano /home/sftpuser1/.ssh/authorized_keys
# Paste public key here

sudo chown -R sftpuser1:sftpusers /home/sftpuser1/.ssh
sudo chmod 700 /home/sftpuser1/.ssh
sudo chmod 600 /home/sftpuser1/.ssh/authorized_keys
Enter fullscreen mode Exit fullscreen mode

You can now disable password login if you wish.

6. Secure the Server

  • Open port 22 to only your office or VPN IP
  • Install fail2ban:
sudo apt install fail2ban
Enter fullscreen mode Exit fullscreen mode
  • Consider using logrotate and basic audit logging

7. Test the Setup

From your local terminal:

sftp -i ~/.ssh/sftpuser1_key sftpuser1@<your-linode-ip>
Enter fullscreen mode Exit fullscreen mode

Then:

cd /upload
put testfile.txt
Enter fullscreen mode Exit fullscreen mode

Tip: SFTP isn’t a shell. You can’t run cat or echo — just put, get, ls, etc.

Why Public Subnet?

Because this server needs to be accessed from the internet. If it were in a private subnet, you’d need a bastion or VPN to reach it — useful for internal automation, but not external sharing.

Just like with your previous setup:

  • The public subnet gives controlled external access
  • Security is enforced via firewall + SSH key access

Lessons Reinforced

  • Chroot directories must be owned by root
  • SFTP can be a secure alternative to email attachments or third-party tools
  • You can still own your file flows in a modern, cloud-native way

Next Steps

For future improvements and personal learning growth:

  • Automate uploads from other services or cron jobs
  • Pipe incoming files into a processing queue (e.g., via inotify or systemd)
  • Back up uploaded files to S3
  • Add a DNS record if you want: sftp.yourdomain.com → your Linode IP

Final Thoughts

Owning your infra doesn't mean reinventing everything — it means understanding the tradeoffs and being able to build what you need, when you need it. This is one more building block toward that confidence.

You’ve got this. Nothing is impossible

Top comments (0)