{"id":62,"date":"2026-04-26T10:15:00","date_gmt":"2026-04-26T07:15:00","guid":{"rendered":"https:\/\/fsaeed.blog\/?p=62"},"modified":"2026-10-31T00:18:50","modified_gmt":"2026-10-30T21:18:50","slug":"securing-a-small-linux-server","status":"publish","type":"post","link":"https:\/\/fsaeed.blog\/index.php\/2026\/04\/26\/securing-a-small-linux-server\/","title":{"rendered":"Securing a Small Linux Server: SSH Keys, Firewall, Updates"},"content":{"rendered":"\n<p class=\"wp-block-paragraph\">Hello again, tech enthusiasts! \ud83d\udc4b<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">You rented a small cloud server for a report script or a website. Great! But within minutes, robots on the internet start trying to log in. They guess passwords day and night.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">The good news: a few simple steps stop almost all of these attacks. Today we lock the front door (SSH keys), build a fence (firewall), and keep the walls fresh (automatic updates). The examples use Ubuntu, but other Linux systems are very similar.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">What you need<\/h3>\n\n\n\n<ul class=\"wp-block-list\">\n<li>A Linux server (Ubuntu 22.04 or 24.04) and a user with <code>sudo<\/code> rights<\/li>\n\n\n<li>A terminal on your own computer (Windows, Mac and Linux all have <code>ssh<\/code> built in)<\/li>\n\n<\/ul>\n\n\n\n<p class=\"wp-block-paragraph\">In the examples, the server is <code>server.example.com<\/code> and the user is <code>admin<\/code>. Change them to yours.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">Step 1: Create an SSH key and copy it to the server<\/h3>\n\n\n\n<p class=\"wp-block-paragraph\">SSH is how you log in to the server from far away. A password is like a door code that anyone can guess. An SSH key is like a real metal key: there are two parts, a private key that stays on your computer and a public key that you give to the server.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Run this on <strong>your own computer<\/strong>, not on the server:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code># Create a modern key. Choose a passphrase when it asks (recommended).\nssh-keygen -t ed25519 -C \"my-laptop\"\n\n# Copy the public key to the server (it asks for your password one last time)\nssh-copy-id admin@server.example.com\n\n# Test: this should log you in without the server password\nssh admin@server.example.com<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">On Windows without <code>ssh-copy-id<\/code>, copy the line from <code>.ssh\\id_ed25519.pub<\/code> into <code>~\/.ssh\/authorized_keys<\/code> on the server.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">Step 2: Turn off password login<\/h3>\n\n\n\n<p class=\"wp-block-paragraph\">Now that the key works, we tell the server to stop accepting passwords. On the server, create a small settings file:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>sudo nano \/etc\/ssh\/sshd_config.d\/00-hardening.conf<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Put these lines in it:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code># Only keys, no passwords\nPasswordAuthentication no\nKbdInteractiveAuthentication no\n# root may log in with a key only, never with a password\nPermitRootLogin prohibit-password<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Why the name <code>00-<\/code>? The SSH server uses the <strong>first<\/strong> value it reads. Files in this folder are read in name order, and some cloud images add a file like <code>50-cloud-init.conf<\/code> that turns passwords back on. A name starting with <code>00-<\/code> is read first, so your choice wins.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Check the settings, then restart the service:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>sudo sshd -t                     # no output means no errors\nsudo systemctl restart ssh\nsudo sshd -T | grep -Ei 'passwordauthentication|permitrootlogin'<\/code><\/pre>\n\n\n\n<h3 class=\"wp-block-heading\">Step 3: Test in a second window before you close anything<\/h3>\n\n\n\n<p class=\"wp-block-paragraph\">This is the most important step. <strong>Keep your current SSH window open.<\/strong> Open a new terminal and log in again:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>ssh admin@server.example.com<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">If it works, great. If not, use the first window to fix the problem. If you close both, you could lock yourself out!<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">Step 4: Turn on the firewall<\/h3>\n\n\n\n<p class=\"wp-block-paragraph\">A firewall is like a guard at the gate. It only lets visitors in through the doors you name. Ubuntu includes a simple one called <strong>ufw<\/strong>.<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>sudo ufw default deny incoming    # block everything coming in...\nsudo ufw default allow outgoing   # ...but let the server reach out\nsudo ufw allow OpenSSH            # IMPORTANT: allow SSH before enabling\nsudo ufw allow 80\/tcp             # only if you run a website\nsudo ufw allow 443\/tcp            # only if you run a website (HTTPS)\nsudo ufw enable\nsudo ufw status verbose<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Only open the ports you really use. A database port should usually stay closed.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">Step 5: Automatic security updates<\/h3>\n\n\n\n<p class=\"wp-block-paragraph\">Most attacks use old, known bugs. Updates fix them. Let the server install security updates by itself:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>sudo apt update\nsudo apt install unattended-upgrades\nsudo dpkg-reconfigure --priority=low unattended-upgrades   # choose \"Yes\"\n\n# Check that it works (this only tests, it changes nothing)\nsudo unattended-upgrade --dry-run --debug<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Some updates need a restart. If the file <code>\/var\/run\/reboot-required<\/code> exists, plan one at a quiet time.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">Bonus: fail2ban<\/h3>\n\n\n\n<p class=\"wp-block-paragraph\">Even with keys only, the robots keep knocking. <strong>fail2ban<\/strong> watches the log and blocks an address for a while after too many failed tries.<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>sudo apt install fail2ban\nsudo nano \/etc\/fail2ban\/jail.local<\/code><\/pre>\n\n\n\n<pre class=\"wp-block-code\"><code>[sshd]\nenabled = true\nmaxretry = 5\nfindtime = 10m\nbantime = 1h<\/code><\/pre>\n\n\n\n<pre class=\"wp-block-code\"><code>sudo systemctl restart fail2ban\nsudo fail2ban-client status sshd<\/code><\/pre>\n\n\n\n<h3 class=\"wp-block-heading\">Common problems<\/h3>\n\n\n\n<ul class=\"wp-block-list\">\n<li><strong>&#8220;Permission denied (publickey)&#8221; after Step 2.<\/strong> The key was not copied correctly. Use your still-open first window and check <code>~\/.ssh\/authorized_keys<\/code>. The folder <code>~\/.ssh<\/code> should have permission <code>700<\/code> and the file <code>600<\/code>.<\/li>\n\n\n<li><strong>Passwords still work.<\/strong> Another file sets <code>PasswordAuthentication yes<\/code>. Run <code>sudo grep -ri password \/etc\/ssh\/sshd_config.d\/<\/code> and check the file names.<\/li>\n\n\n<li><strong>The website stopped working after ufw.<\/strong> You forgot to allow 80 or 443. Add them with <code>sudo ufw allow<\/code>.<\/li>\n\n\n<li><strong>fail2ban does not start.<\/strong> Some systems have no <code>\/var\/log\/auth.log<\/code>. Add <code>backend = systemd<\/code> under <code>[sshd]<\/code> and restart.<\/li>\n\n<\/ul>\n\n\n\n<h3 class=\"wp-block-heading\">Wrap-up<\/h3>\n\n\n\n<p class=\"wp-block-paragraph\">With keys, a firewall, automatic updates and fail2ban, your server is now much safer than many servers on the internet.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Next idea: set up a daily backup of your server to another place. A backup protects you from everything else, including your own mistakes. \ud83d\ude42<\/p>\n","protected":false},"excerpt":{"rendered":"<p>Five simple steps to protect your small Linux server from the most common attacks.<\/p>\n","protected":false},"author":1,"featured_media":0,"comment_status":"closed","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[1],"tags":[59,27,32,58,60],"class_list":["post-62","post","type-post","status-publish","format-standard","hentry","category-uncategorized","tag-firewall","tag-linux","tag-security","tag-ssh","tag-ubuntu"],"_links":{"self":[{"href":"https:\/\/fsaeed.blog\/index.php\/wp-json\/wp\/v2\/posts\/62","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/fsaeed.blog\/index.php\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/fsaeed.blog\/index.php\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/fsaeed.blog\/index.php\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/fsaeed.blog\/index.php\/wp-json\/wp\/v2\/comments?post=62"}],"version-history":[{"count":1,"href":"https:\/\/fsaeed.blog\/index.php\/wp-json\/wp\/v2\/posts\/62\/revisions"}],"predecessor-version":[{"id":71,"href":"https:\/\/fsaeed.blog\/index.php\/wp-json\/wp\/v2\/posts\/62\/revisions\/71"}],"wp:attachment":[{"href":"https:\/\/fsaeed.blog\/index.php\/wp-json\/wp\/v2\/media?parent=62"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/fsaeed.blog\/index.php\/wp-json\/wp\/v2\/categories?post=62"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/fsaeed.blog\/index.php\/wp-json\/wp\/v2\/tags?post=62"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}