<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>Forem: Tharuka Dilshara</title>
    <description>The latest articles on Forem by Tharuka Dilshara (@dilsharatharuka52web).</description>
    <link>https://forem.com/dilsharatharuka52web</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F3874970%2Fb685c335-2886-44f6-8666-e59bd0746a6c.jpeg</url>
      <title>Forem: Tharuka Dilshara</title>
      <link>https://forem.com/dilsharatharuka52web</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://forem.com/feed/dilsharatharuka52web"/>
    <language>en</language>
    <item>
      <title>How I Built an Automated Linux Hardening Script as a Complete Beginner</title>
      <dc:creator>Tharuka Dilshara</dc:creator>
      <pubDate>Mon, 13 Apr 2026 13:35:26 +0000</pubDate>
      <link>https://forem.com/dilsharatharuka52web/how-i-built-an-automated-linux-hardening-script-as-a-complete-beginner-2l22</link>
      <guid>https://forem.com/dilsharatharuka52web/how-i-built-an-automated-linux-hardening-script-as-a-complete-beginner-2l22</guid>
      <description>&lt;h2&gt;
  
  
  Why I Built This
&lt;/h2&gt;

&lt;p&gt;The first time I deployed a Linux server I made the same mistake thousands of developers make.&lt;/p&gt;

&lt;p&gt;I thought security was something for advanced users. Something complicated. Something I would handle later.&lt;/p&gt;

&lt;p&gt;Within hours of my server going live I checked the logs and saw thousands of failed login attempts. Bots were scanning my ports. Automated scripts were trying common username and password combinations over and over again.&lt;/p&gt;

&lt;p&gt;My server was a brand new house with every door and window wide open.&lt;br&gt;
That experience changed how I think about servers forever. Security is not optional. It is foundational. And it needs to happen on day one — not day thirty.&lt;/p&gt;

&lt;p&gt;So I built an automated hardening script. One script that takes a completely unsecured Linux server and locks it down automatically.&lt;br&gt;
This is the complete story of how I built it, what I learned, and how you can build it too.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F3uluvqw90rqcn8vrmm77.jpeg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F3uluvqw90rqcn8vrmm77.jpeg" alt="creenshot of failed login attempts in server logs"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;h2&gt;
  
  
  What is a Hardening Script?
&lt;/h2&gt;

&lt;p&gt;A hardening script is a bash file — basically a list of commands — that runs automatically and secures your server without you having to type each command manually.&lt;/p&gt;

&lt;p&gt;Instead of doing this every time you set up a new server:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Step 1 — open SSH config&lt;br&gt;
Step 2 — disable root login&lt;br&gt;
Step 3 — disable password auth&lt;br&gt;
Step 4 — install firewall&lt;br&gt;
Step 5 — configure firewall rules&lt;br&gt;
Step 6 — install fail2ban&lt;br&gt;
Step 7 — configure fail2ban&lt;br&gt;
Step 8 — enable auto updates&lt;br&gt;
... and 10 more steps&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;You just run one command:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;sudo &lt;/span&gt;bash hardening.sh
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And everything happens automatically in under 60 seconds.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Four Security Problems This Solves
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fkcqtpm7h4wjv968mj57p.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fkcqtpm7h4wjv968mj57p.png" alt="Simple diagram showing 4 security layers"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Problem 1 — SSH is Wide Open&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;SSH is how you connect to your server remotely. By default any person on the internet can try to log in using any username and any password as many times as they want.&lt;/p&gt;

&lt;p&gt;Attackers use automated bots that try thousands of common username and password combinations every hour.&lt;/p&gt;

&lt;p&gt;The fix: Disable password login completely. Force key-based authentication only. Disable root login. Limit login attempts to 3.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Problem 2 — No Firewall&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;A fresh Linux server has thousands of open ports. Each open port is a potential door for attackers.&lt;/p&gt;

&lt;p&gt;The fix: Install UFW firewall. Block everything by default. Only open the 3 ports you actually need — SSH, HTTP, and HTTPS.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Problem 3 — No Attack Detection&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Without monitoring nobody tells you when someone is actively trying to break into your server.&lt;/p&gt;

&lt;p&gt;The fix: Install fail2ban. It watches your logs 24/7 and automatically bans any IP address that fails to login too many times.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Problem 4 — Unpatched Software&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;New security vulnerabilities are discovered every day. If your server software is not updated regularly it becomes vulnerable to known attacks.&lt;/p&gt;

&lt;p&gt;The fix: Enable unattended-upgrades. Your server automatically downloads and installs security patches every night.&lt;/p&gt;

&lt;h2&gt;
  
  
  What I Built — The Complete Script
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fo2jgqju3p6kqh5qmslse.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fo2jgqju3p6kqh5qmslse.png" alt=" Screenshot of your hardening.sh file open in VS Code"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The Foundation&lt;/p&gt;

&lt;p&gt;Every good script starts with a solid foundation. Mine has three parts:&lt;/p&gt;

&lt;p&gt;Colored output so you can instantly see what is working and what is not. Green means success. Red means error. Yellow means warning.&lt;/p&gt;

&lt;p&gt;Root check that stops the script immediately if you forgot to run it with admin privileges.&lt;/p&gt;

&lt;p&gt;Dry run mode that lets you test the script safely without making any real changes. Just add --dry-run when running and it prints what it would do instead of actually doing it.&lt;/p&gt;

&lt;h2&gt;
  
  
  Section 1 — SSH Hardening
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;&amp;gt; Internet → [Blocked ports ❌] → Server
           [Allowed ports ✅ (22, 80, 443)]

&amp;gt; 
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;SSH is the front door of your server. This section locks it down properly.&lt;/p&gt;

&lt;p&gt;The script makes three critical changes to the SSH configuration file:&lt;/p&gt;

&lt;p&gt;Disables root login — Attackers cannot target the most powerful user directly anymore. They would need to know your specific username first.&lt;/p&gt;

&lt;p&gt;Disables password authentication — No more password guessing attacks. The only way to connect is with an SSH key pair — a much stronger form of authentication.&lt;/p&gt;

&lt;p&gt;Limits login attempts — Maximum 3 tries before the connection is cut. Combined with fail2ban this makes brute force attacks practically impossible.&lt;/p&gt;

&lt;p&gt;After making these changes the script automatically restarts SSH to apply them.&lt;/p&gt;

&lt;p&gt;Important warning: Always test SSH access in a new terminal window before closing your current session. If something goes wrong you want to be able to fix it while still connected.&lt;/p&gt;

&lt;h2&gt;
  
  
  Section 2 — UFW Firewall
&lt;/h2&gt;

&lt;p&gt;UFW stands for Uncomplicated Firewall. It is the most beginner friendly way to manage firewall rules on Ubuntu and Debian.&lt;/p&gt;

&lt;p&gt;The script configures it with three simple rules:&lt;/p&gt;

&lt;p&gt;Block all incoming traffic by default — Every connection attempt is rejected unless it matches a specific rule you create.&lt;/p&gt;

&lt;p&gt;Allow outgoing traffic — Your server can still connect to the internet to download updates and send responses.&lt;/p&gt;

&lt;p&gt;Open only 3 specific ports — SSH on port 22, HTTP on port 80, and HTTPS on port 443. Everything else stays closed.&lt;/p&gt;

&lt;p&gt;One critical thing to always remember — allow SSH before enabling the firewall. If you enable UFW first and forget to allow SSH you will lock yourself out completely.&lt;/p&gt;

&lt;p&gt;Important note for Docker users: UFW and Docker do not work together the way most people assume. Docker creates its own firewall rules that UFW does not control. Always test your container ports manually after setting up UFW.&lt;/p&gt;

&lt;h2&gt;
  
  
  Section 3 — fail2ban
&lt;/h2&gt;

&lt;p&gt;ail2ban is one of the simplest and most effective security tools available for Linux servers.&lt;/p&gt;

&lt;p&gt;It works by watching your log files. Every time an IP address fails to authenticate too many times — fail2ban automatically adds a rule to block that IP address for a set period of time.&lt;/p&gt;

&lt;p&gt;The script configures three key settings:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;bantime = 3600 — Banned IPs are blocked for 1 hour&lt;br&gt;
findtime = 600 — Counts failures within a 10 minute window&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;maxretry = 5 — Bans after 5 failed attempts&lt;br&gt;
On a publicly accessible server you will typically see multiple banned IP addresses within just a few hours of going live. fail2ban handles all of this silently in the background without you doing anything.&lt;/p&gt;
&lt;h2&gt;
  
  
  Section 4 — Automatic Updates
&lt;/h2&gt;

&lt;p&gt;The most boring security measure is also one of the most effective.&lt;/p&gt;

&lt;p&gt;Keeping your software updated patches known vulnerabilities before attackers can exploit them. The gap between a vulnerability being discovered and a patch being released is short. The gap between the patch being released and you actually installing it is where most server breaches happen.&lt;/p&gt;

&lt;p&gt;unattended-upgrades solves this by automatically downloading and installing security patches every night.&lt;/p&gt;

&lt;p&gt;One important limitation: unattended-upgrades only covers your operating system packages. It does not update Docker images. If you run containers you need to handle those separately using a tool like Watchtower.&lt;/p&gt;
&lt;h2&gt;
  
  
  The Professional Features
&lt;/h2&gt;

&lt;p&gt;What makes this more than just a collection of commands are the professional features built into the script.&lt;/p&gt;

&lt;p&gt;Idempotency means the script can be run multiple times safely. Before making any change it checks whether that change has already been made. If SSH root login is already disabled it skips that step instead of trying to disable it again. This prevents the script from breaking things if you run it twice.&lt;/p&gt;

&lt;p&gt;Dry run mode lets you see exactly what the script would do before it does anything. Run it with --dry-run and it prints every action without executing any of them. This is essential for safely testing on production servers.&lt;/p&gt;

&lt;p&gt;Colored output makes the results easy to read at a glance. You do not have to read through walls of text to see if something failed.&lt;/p&gt;
&lt;h2&gt;
  
  
  How to Run It
&lt;/h2&gt;


&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Test first — always&lt;/span&gt;
&lt;span class="nb"&gt;sudo &lt;/span&gt;bash hardening.sh &lt;span class="nt"&gt;--dry-run&lt;/span&gt;

&lt;span class="c"&gt;# When you are happy with what it shows&lt;/span&gt;
&lt;span class="nb"&gt;sudo &lt;/span&gt;bash hardening.sh
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;After running verify everything worked:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Check firewall is on&lt;/span&gt;
&lt;span class="nb"&gt;sudo &lt;/span&gt;ufw status verbose

&lt;span class="c"&gt;# Check fail2ban is running&lt;/span&gt;
&lt;span class="nb"&gt;sudo &lt;/span&gt;fail2ban-client status sshd

&lt;span class="c"&gt;# Check what ports are open&lt;/span&gt;
ss &lt;span class="nt"&gt;-tlpn&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  What Runs Automatically After Setup
&lt;/h2&gt;

&lt;p&gt;Once you run the script once you do not need to run it again. Here is what keeps working automatically:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fitm2vmxcw49bbl0rwq97.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fitm2vmxcw49bbl0rwq97.png" alt="fiturse"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  What I Learned Building This
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;Security is contextual.&lt;br&gt;
There is no universal checklist. What works for one server may not work for another. Understand your specific setup before applying anyone else's commands.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Test before you apply.&lt;br&gt;
The dry run flag saved me multiple times. Always know what your script will do before it does it.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Order matters.&lt;br&gt;
Allow SSH before enabling UFW. Back up config files before editing them. Test in a new terminal before closing your current session. Small things in the wrong order cause big problems.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Simple tools work.&lt;br&gt;
fail2ban is not sophisticated. UFW is called Uncomplicated Firewall for a reason. You do not need complex enterprise tools to secure a personal server. Simple tools applied correctly are very effective.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Security is never finished.&lt;br&gt;
This script is a strong starting point. But security is an ongoing practice — not a one time setup. Check your logs regularly. Keep learning.&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  Resources
&lt;/h2&gt;

&lt;p&gt;Learning bash scripting:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Linux Command Line book by William Shotts — free online at linuxcommand.org&lt;/li&gt;
&lt;li&gt;Bash scripting tutorial at ryanstutorials.net&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Learning about server security:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;DigitalOcean community tutorials — digitalocean.com/community&lt;/li&gt;
&lt;li&gt;Linux security documentation — linux.die.net&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Tools used in this project:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Ubuntu 22.04 LTS&lt;/li&gt;
&lt;li&gt;UFW — built into Ubuntu&lt;/li&gt;
&lt;li&gt;fail2ban — apt install fail2ban&lt;/li&gt;
&lt;li&gt;unattended-upgrades — apt install unattended-upgrades&lt;/li&gt;
&lt;li&gt;VS Code with Remote SSH extension&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Free servers to practice on:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Oracle Cloud free tier — cloud.oracle.com&lt;/li&gt;
&lt;li&gt;GitHub Student Pack — education.github.com&lt;/li&gt;
&lt;li&gt;Google Cloud free trial — cloud.google.com&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Get the Code
&lt;/h2&gt;

&lt;p&gt;The complete script is available on GitHub:&lt;br&gt;
🔗 &lt;a href="https://github.com/dilsharatharuka52-web/Automated-Linux-Hardening-Script.git" rel="noopener noreferrer"&gt;https://github.com/dilsharatharuka52-web/Automated-Linux-Hardening-Script.git&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The repository includes:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Complete hardening script with all sections&lt;/li&gt;
&lt;li&gt;README with setup instructions&lt;/li&gt;
&lt;li&gt;Checklist of everything the script does&lt;/li&gt;
&lt;li&gt;Example output screenshots&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Final Thoughts
&lt;/h2&gt;

&lt;p&gt;Building this project taught me more about Linux security in one week than months of reading about it.&lt;/p&gt;

&lt;p&gt;The best way to learn security is to actually secure something.&lt;/p&gt;

&lt;p&gt;Start with a free server from Oracle Cloud. Run the script. Read the output. Check what changed. Break things and fix them.&lt;/p&gt;

&lt;p&gt;Security feels overwhelming until you start doing it. Then it becomes one of the most satisfying parts of working with servers.&lt;/p&gt;

&lt;p&gt;If you have questions about any part of this project leave a comment below. I am happy to help.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/..." class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/..." alt="profile"&gt;&lt;/a&gt;&lt;/p&gt;

</description>
      <category>linux</category>
      <category>cybersecurity</category>
      <category>devops</category>
      <category>programming</category>
    </item>
    <item>
      <title>👋 Hi, I’m Tharuka</title>
      <dc:creator>Tharuka Dilshara</dc:creator>
      <pubDate>Sun, 12 Apr 2026 14:03:42 +0000</pubDate>
      <link>https://forem.com/dilsharatharuka52web/hi-im-tharuka-76c</link>
      <guid>https://forem.com/dilsharatharuka52web/hi-im-tharuka-76c</guid>
      <description>&lt;p&gt;I’m a DevOps learner and builder who is trying to grow step by step in cloud, automation, and AI.&lt;/p&gt;

&lt;p&gt;Right now, I’m focused on learning:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Docker &amp;amp; Kubernetes&lt;/li&gt;
&lt;li&gt;CI/CD pipelines&lt;/li&gt;
&lt;li&gt;Cloud platforms (AWS, GCP, Azure)&lt;/li&gt;
&lt;li&gt;MLOps tools like MLflow and Kubeflow&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;I don’t come from a perfect background, and I’m not an expert yet. I’m still learning every day. But I believe in one thing: consistency beats everything.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;🚀 Why I’m here&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;I started this journey because I love building things. Not just websites—but systems that run automatically, scale, and solve real problems.&lt;/p&gt;

&lt;p&gt;My goal is to become a strong DevOps &amp;amp; MLOps engineer and build real-world projects that help businesses.&lt;/p&gt;

&lt;p&gt;🛠 What I’m doing now&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Building small DevOps projects&lt;/li&gt;
&lt;li&gt;Practicing Linux and Python&lt;/li&gt;
&lt;li&gt;Learning infrastructure and automation&lt;/li&gt;
&lt;li&gt;Sharing everything I learn&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;📌 My mindset&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;I don’t wait until I’m “ready.”&lt;/li&gt;
&lt;li&gt;I build, fail, fix, and improve.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;🤝 Let’s connect&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;If you’re also learning DevOps, cloud, or AI—let’s grow together.&lt;/p&gt;

</description>
      <category>devops</category>
      <category>ai</category>
      <category>programming</category>
      <category>automation</category>
    </item>
  </channel>
</rss>
