<?xml version="1.0" encoding="UTF-8"?><rss xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:atom="http://www.w3.org/2005/Atom" version="2.0"><channel><title><![CDATA[Victor's Codex]]></title><description><![CDATA[Victor's Codex]]></description><link>https://blog.victoriliya.com</link><generator>RSS for Node</generator><lastBuildDate>Thu, 30 Apr 2026 12:00:09 GMT</lastBuildDate><atom:link href="https://blog.victoriliya.com/rss.xml" rel="self" type="application/rss+xml"/><language><![CDATA[en]]></language><ttl>60</ttl><item><title><![CDATA[Managing Multiple Environments in Terraform with a Single Repository]]></title><description><![CDATA[Terraform is a powerful tool for defining infrastructure as code (IaC). When managing multiple environments like dev, staging, and prod, organizing your Terraform code effectively is key.
In this article, I’ll share how I manage multiple environments...]]></description><link>https://blog.victoriliya.com/managing-multiple-environments-in-terraform-with-a-single-repository</link><guid isPermaLink="true">https://blog.victoriliya.com/managing-multiple-environments-in-terraform-with-a-single-repository</guid><category><![CDATA[Terraform]]></category><category><![CDATA[ci-cd]]></category><dc:creator><![CDATA[VICTOR ILIYA]]></dc:creator><pubDate>Fri, 23 May 2025 09:03:13 GMT</pubDate><content:encoded><![CDATA[<p>Terraform is a powerful tool for defining infrastructure as code (IaC). When managing multiple environments like dev, staging, and prod, organizing your Terraform code effectively is key.</p>
<p>In this article, I’ll share how I manage multiple environments using a single Git repository with environment-specific folders. This approach is simple, scalable, and keeps everything in one place.</p>
<h2 id="heading-why-a-single-repository">Why a Single Repository?</h2>
<p>Using one repository for all environments simplifies maintenance and ensures consistency. It allows you to:</p>
<ul>
<li><p>Reuse shared modules across environments.</p>
</li>
<li><p>Centralize version control and code reviews.</p>
</li>
<li><p>Streamline CI/CD pipelines.</p>
</li>
</ul>
<p>Each environment has its own configuration and state file, ensuring isolation while keeping the codebase unified.</p>
<p><strong>NOTE: This is just one method. Of course, many organizations may have different ways of managing different environments such as this or having separate repos for the environments altogether for better isolation.</strong></p>
<h2 id="heading-folder-structure">Folder Structure</h2>
<pre><code class="lang-markdown">terraform/
├── environments/
│   ├── dev/
│   │   ├── main.tf
│   │   ├── variables.tf
│   │   ├── outputs.tf
│   │   ├── terraform.tfvars
│   │   └── backend.tf
│   ├── staging/
│   │   ├── main.tf
│   │   ├── variables.tf
│   │   ├── outputs.tf
│   │   ├── terraform.tfvars
│   │   └── backend.tf
│   └── prod/
│       ├── main.tf
│       ├── variables.tf
│       ├── outputs.tf
│       ├── terraform.tfvars
│       └── backend.tf
├── modules/
│   ├── ec2/
│   ├── sqs/
│   └── s3/
├── main.tf
├── variables.tf
├── outputs.tf
├── terraform.tfvars
├── .gitignore
└── README.md
</code></pre>
<h2 id="heading-how-it-works">How It Works</h2>
<ol>
<li><p><strong>Environment-Specific Configurations</strong>: Each environment folder contains:</p>
<ul>
<li><p><strong>main.tf</strong>: Defines resources, referencing modules.</p>
</li>
<li><p><strong>variables.tf</strong>: Defines input variables.</p>
</li>
<li><p><strong>outputs.tf</strong>: Specifies output values.</p>
</li>
<li><p><strong>terraform.tfvars</strong>: Sets environment-specific variable values (e.g., region, instance size).</p>
</li>
<li><p><strong>backend.tf</strong>: Configures the state file storage.</p>
</li>
</ul>
</li>
<li><p><strong>State File Management</strong>: Terraform stores the state of each environment in a separate state file. I use an AWS S3 bucket to store state files remotely, with different keys for each environment (e.g., dev/terraform.tfstate, prod/terraform.tfstate). S3 now supports native state locking, thus dynamodb will not be used.</p>
</li>
<li><p><strong>Modules for Reusability</strong>: Shared infrastructure (e.g., VPCs) is defined in the modules/ folder. Each environment’s main.tf references these modules with environment-specific inputs:</p>
</li>
</ol>
<h2 id="heading-best-practices">Best Practices</h2>
<ul>
<li><p><strong>Version Control</strong>: Store the repository in Git (e.g., GitHub). Use pull requests for code reviews.</p>
</li>
<li><p><strong>State Security</strong>: Enable S3 bucket versioning and encryption and restrict access with bucket policies.</p>
</li>
<li><p><strong>CI/CD Integration</strong>: Use a pipeline (e.g., GitHub Actions) to run terraform plan and apply for each environment. Require manual approval for prod.</p>
</li>
<li><p><strong>Consistency</strong>: Use terraform fmt for formatting and terraform validate for syntax checks.</p>
</li>
<li><p><strong>Documentation</strong>: Add a README.md in each module and environment folder.</p>
</li>
</ul>
]]></content:encoded></item><item><title><![CDATA[How to Connect to an EC2 Instance in a Private Network Using a Bastion Host]]></title><description><![CDATA[Introduction
In cloud environments like AWS, it's a best practice to place your application servers in private subnets to prevent direct access from the internet. But then, how do you connect to those private instances for troubleshooting or configur...]]></description><link>https://blog.victoriliya.com/how-to-connect-to-an-ec2-instance-in-a-private-network-using-a-bastion-host</link><guid isPermaLink="true">https://blog.victoriliya.com/how-to-connect-to-an-ec2-instance-in-a-private-network-using-a-bastion-host</guid><category><![CDATA[ec2]]></category><category><![CDATA[vpc]]></category><category><![CDATA[subnet]]></category><category><![CDATA[Bastion Hosts]]></category><category><![CDATA[AWS]]></category><category><![CDATA[Devops]]></category><dc:creator><![CDATA[VICTOR ILIYA]]></dc:creator><pubDate>Thu, 17 Apr 2025 21:07:36 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1744925380275/caaea6ed-de38-4986-aab3-6d1944cdf096.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<h1 id="heading-introduction">Introduction</h1>
<p>In cloud environments like AWS, it's a best practice to place your application servers in private subnets to prevent direct access from the internet. But then, how do you connect to those private instances for troubleshooting or configuration?</p>
<p>This is where a <strong>Bastion Host</strong> comes in, a secure jump server that provides SSH access to private instances.</p>
<h2 id="heading-in-this-post-youll-learn-step-by-step-how-to">In this post, you’ll learn step-by-step how to:</h2>
<ul>
<li><p>Configure your VPC, security groups and route tables</p>
</li>
<li><p>Set up a bastion host</p>
</li>
<li><p>SSH into a private EC2 instance via the bastion host</p>
</li>
</ul>
<h2 id="heading-prerequisites">Prerequisites</h2>
<p>Before we start, ensure the following:</p>
<ul>
<li><p>You have an AWS account</p>
</li>
<li><p>You’re familiar with basic VPC, EC2, and SSH concepts <strong>(optional, but helpful)</strong></p>
</li>
</ul>
<h1 id="heading-architecture-overview">Architecture Overview</h1>
<p>You’ll create a simple VPC setup like this:</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1744865296024/d023843d-065e-4607-a07d-644454399615.png" alt="Bastion host Architecture" class="image--center mx-auto" /></p>
<h3 id="heading-step-1-create-your-vpc-and-subnets">STEP 1: Create Your VPC and Subnets</h3>
<ol>
<li><p><strong>Create a VPC</strong></p>
<ul>
<li><p>Enter a CIDR block (e.g., 10.0.0.0/16 the range of IP addresses your VPC will have)</p>
</li>
<li><p>Give your VPC a name (always ensure you tag your resources)</p>
</li>
</ul>
</li>
<li><p><strong>Create 2 Subnets</strong>:</p>
<ul>
<li><p>Public Subnet (e.g., 10.0.1.0/24)</p>
</li>
<li><p>Private Subnet (e.g., 10.0.2.0/24)</p>
</li>
</ul>
</li>
<li><p><strong>Create an Internet Gateway (IGW)</strong>:</p>
<ul>
<li><p>This will give internet access to the public subnets within the VPC and then in the action’s menu.</p>
</li>
<li><p>Once the Internet Gateway is created, click on the <strong>“Actions”</strong> button on the top-right, and attach to VPC. Ensure you select your created VPC, your tags should make this easier to find.</p>
</li>
</ul>
</li>
<li><p><strong>Update route table</strong></p>
<p> Create a route to destination (0.0.0.0./0) and the target should be the Internet Gateway you Attached to the VPC.</p>
</li>
<li><p><strong>Create the Private Route table</strong></p>
<p> What makes a route table private is the absence of a route to outside traffic through an Internet Gateway. This is what’d needed for the private subnet.</p>
</li>
<li><p><strong>Create a route table association for the Private Subnet.</strong></p>
<p> <strong>NOTE:</strong> By default, both subnets are implicitly associated to the VPC’s main route table, and this would give them internet access, but only one requires it, so another route table will be made for the private subnet.</p>
</li>
</ol>
<p>By the time you’re done with the above steps, you have the following in your VPC resource map.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1744901803861/49ca06ca-39f3-4ece-9a53-984a0ace508e.png" alt class="image--center mx-auto" /></p>
<hr />
<h3 id="heading-step-2-launch-the-bastion-host-public-instance">Step 2: Launch the Bastion Host (Public Instance)</h3>
<ol>
<li><p>Launch an EC2 instance (Amazon Linux/Ubuntu) in the public subnet.</p>
</li>
<li><p>Assign it a public IP</p>
</li>
<li><p>Associate it with a security group that:</p>
<ul>
<li>Allows SSH (port 22) from your IP</li>
</ul>
</li>
<li><p>Use a key pair for SSH access, it will automatically download. Ensure you take note of its location.</p>
</li>
</ol>
<h3 id="heading-step-3-launch-the-private-instance-private-subnet">Step 3: Launch the Private Instance (Private Subnet)</h3>
<ol>
<li><p>Launch an EC2 instance in the private subnet</p>
</li>
<li><p>Do <strong>not</strong> assign a public IP</p>
</li>
<li><p>Configure your security group to:</p>
<ul>
<li>Allows SSH (port 22) from the bastion host’s security group</li>
</ul>
</li>
<li><p>Select a key-pair, just as in the previous instance.</p>
</li>
</ol>
<h3 id="heading-step-4-ssh-into-the-private-instance-via-bastion">Step 4: SSH Into the Private Instance via Bastion</h3>
<p>Now let’s connect:</p>
<ol>
<li><p><strong>SSH into the Bastion Host.</strong></p>
<ul>
<li><p>Go the Instances dashboard and select the instance your created</p>
</li>
<li><p>Go to the Connect option on the top-right and click on SSH client. Now copy the command and paste it in your terminal as such.</p>
</li>
</ul>
</li>
</ol>
<pre><code class="lang-bash">ssh -i <span class="hljs-string">"test.pem"</span> ec2-user@54.165.42.99
</code></pre>
<p><strong>NOTE:</strong> <code>test.pem</code> was what I saved my key-pair as.</p>
<p>If you’re on a Linux distro such as ubuntu you have to manage the permissions of your key-pair using the following command.</p>
<pre><code class="lang-bash">chmod 400 <span class="hljs-string">"test.pem"</span>
</code></pre>
<p>Once you’ve SHH’d into the into the instance, you should get something like below</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1744918017140/e6efdcb1-8ef9-44c7-992e-965f61dffdca.png" alt class="image--center mx-auto" /></p>
<p><strong>Security concerns:</strong> The key-pair and instances used were strictly created for this project and have since been deleted.</p>
<ul>
<li><p><strong>SSH into the Private EC2 Instance.</strong></p>
<p>  <strong>NOTE:</strong> The key-pair for your private instance was download unto your local PC, so your instance does not have it. In order to SSH into your private subnet, we’d need to copy the content of the key pair to your Bastion Host.</p>
<ul>
<li><p>Open your key-pair file for your private instance saved to your local PC and copy all of its contents using <code>CTRL + a</code> and <code>CTRL + c</code></p>
</li>
<li><p>Now go to your Bastion Host, presumably still SHH’d into and perform the following:</p>
</li>
<li><pre><code class="lang-bash">      vim test.pem
</code></pre>
<ol start="2">
<li>A screen like such below will come up</li>
</ol>
</li>
</ul>
</li>
</ul>
<p>        <img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1744919658791/18c59e73-69ce-40e0-97c3-0370f2ae427c.png" alt class="image--center mx-auto" /></p>
<ol start="3">
<li><p>Press <code>i</code> on your keyboard to edit, and then paste your key-pair copied in previous steps.</p>
</li>
<li><p>Now to save the file press <code>ESC</code> on your keyboard and type <code>:wq!</code> to save and exit the vim editor.</p>
</li>
<li><p>To confirm if your keypair saved, use the following to view its contents</p>
</li>
<li><pre><code class="lang-bash">   cat test.pem
</code></pre>
</li>
<li><p>Now modify the permissions for the key pair</p>
</li>
<li><pre><code class="lang-bash">   chmod 400 <span class="hljs-string">"test.pem"</span>
</code></pre>
</li>
</ol>
<ul>
<li><p>Now, just like your bastion host, go to the EC2 dashboard, and click on “connect” on the top-right corner, click ln the “SSH Client” and then copy the command. Paste it into your Bastion Hosts terminal.</p>
</li>
<li><pre><code class="lang-bash">      ssh -i <span class="hljs-string">"test.pem"</span> ec2-user@10.0.0.11
</code></pre>
</li>
</ul>
<p>    <img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1744921254003/5cee5d30-cc9d-4d6e-9007-c32e571d325e.png" alt class="image--center mx-auto" /></p>
<p>    This should display the above, indicating you’ve SSH’d into your private subnet</p>
<h3 id="heading-congratulations">CONGRATULATIONS!!🥂</h3>
<p>You have successfully and securely accessed your private instance using a bastion host, now you can configure your instance how you want</p>
<p><strong>NOTE: This method does not give your private instance internet access to download resources.</strong></p>
<p><strong>NOTE: Using</strong> <strong>Session Manager (SSM) is better for enhanced, will be discussed in a future post</strong></p>
<hr />
<h3 id="heading-commonly-faced-errors">COMMONLY FACED ERRORS</h3>
<ol>
<li><p><strong>Timeout When SSHing to Private EC2</strong></p>
<ul>
<li><p><strong>Cause:</strong> Private security group doesn't allow SSH from Bastion hosts security group</p>
</li>
<li><p><strong>Fix:</strong> Allow port 22 from <strong>Bastion's Security group</strong> in Private EC2's security group</p>
</li>
</ul>
</li>
<li><p><strong>Permission Denied (publickey)</strong></p>
<ul>
<li><p><strong>Cause:</strong> Wrong key or wrong permissions</p>
</li>
<li><p><strong>Fix:</strong> Use correct <code>.pem</code> file and/or run <code>chmod 400 my-key.pem</code></p>
</li>
</ul>
</li>
<li><p><strong>Can’t Reach Bastion Host</strong></p>
<ul>
<li><p><strong>Cause:</strong> No public IP, IGW, or incorrect route table</p>
</li>
<li><p><strong>Fix:</strong> Ensure the bastion host has a <strong>public IP</strong>, IGW attached to the VPC, and <strong>route to 0.0.0.0/0</strong> on the route table.</p>
</li>
</ul>
</li>
</ol>
<hr />
<h3 id="heading-best-practices">BEST PRACTICES</h3>
<ul>
<li><p>Use <strong>key rotation</strong> and minimal SSH access</p>
</li>
<li><p>Set up <strong>CloudWatch</strong> for monitoring the bastion</p>
</li>
<li><p>Consider using <strong>Session Manager (SSM)</strong> for enhanced security (no SSH needed)</p>
</li>
<li><p>Restrict bastion access with <strong>security groups and NACLs</strong></p>
</li>
</ul>
<hr />
<h3 id="heading-conclusion">CONCLUSION</h3>
<p>Using a bastion host is a secure and controlled way to access EC2 instances in a private network. This setup is essential for production-grade cloud environments.</p>
<p>Stay tuned for a future post where we’ll automate this entire setup using <strong>Terraform</strong> or <strong>CloudFormation</strong>!</p>
]]></content:encoded></item><item><title><![CDATA[HNG DevOps Stage - 2]]></title><description><![CDATA[Introduction
As part of my HNG DevOps Internship, I was tasked with deploying a FastAPI application with a Continuous Integration (CI) and Continuous Deployment (CD) pipeline while ensuring the application is served using Nginx as a reverse proxy. Th...]]></description><link>https://blog.victoriliya.com/hng-devops-stage-2</link><guid isPermaLink="true">https://blog.victoriliya.com/hng-devops-stage-2</guid><category><![CDATA[Hnginternship]]></category><category><![CDATA[FastAPI]]></category><category><![CDATA[CI/CD]]></category><category><![CDATA[ci-cd]]></category><dc:creator><![CDATA[VICTOR ILIYA]]></dc:creator><pubDate>Sat, 22 Feb 2025 18:48:50 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1740250326558/b8ef381f-1c33-42e8-9ce5-70beeb742576.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<h2 id="heading-introduction">Introduction</h2>
<p>As part of my HNG DevOps Internship, I was tasked with deploying a <strong>FastAPI application</strong> with a <strong>Continuous Integration (CI) and Continuous Deployment (CD) pipeline</strong> while ensuring the application is served using <strong>Nginx as a reverse proxy</strong>. This blog post will guide you step by step on how to complete this project.</p>
<h2 id="heading-project-overview"><strong>Project Overview</strong></h2>
<p>In this task, we:</p>
<ol>
<li><p>Implement a missing API endpoint.</p>
</li>
<li><p>Set up a CI pipeline to run tests using <code>pytest</code>.</p>
</li>
<li><p>Configure a CD pipeline to deploy on merging into <code>main</code>.</p>
</li>
<li><p>Dockerize the FastAPI application.</p>
</li>
<li><p>Serve the application using Nginx.</p>
</li>
</ol>
<hr />
<h2 id="heading-step-1-fork-the-template-repository"><strong>Step 1: Fork the Template Repository</strong></h2>
<ol>
<li><p>Navigate to the given template repository on GitHub.</p>
</li>
<li><p>Click on <strong>Fork</strong> to create a copy under your GitHub account.</p>
</li>
<li><p>Clone your forked repository to your local machine:</p>
<pre><code class="lang-sh"> git <span class="hljs-built_in">clone</span> https://github.com/your-username/fastapi-project.git
 <span class="hljs-built_in">cd</span> fastapi-project
</code></pre>
</li>
</ol>
<hr />
<p><strong>NOTE: THIS REPO IS NO LONGER UP.</strong></p>
<h2 id="heading-step-2-implement-the-missing-endpoint"><strong>Step 2: Implement the Missing Endpoint</strong></h2>
<p>We need to add an endpoint to retrieve a book by its ID.</p>
<h3 id="heading-modify-routespyhttproutespy"><strong>Modify</strong> <a target="_blank" href="http://routes.py"><code>routes.py</code></a></h3>
<ol>
<li><p>Open the <a target="_blank" href="http://routes.py"><code>routes.py</code></a> file.</p>
</li>
<li><p>Add the following function:</p>
<pre><code class="lang-python"> <span class="hljs-keyword">from</span> fastapi <span class="hljs-keyword">import</span> FastAPI, HTTPException
 <span class="hljs-keyword">from</span> models <span class="hljs-keyword">import</span> books  <span class="hljs-comment"># Assuming books is a predefined list or database object</span>

 app = FastAPI()

<span class="hljs-meta"> @app.get("/api/v1/books/{book_id}")</span>
 <span class="hljs-keyword">async</span> <span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">get_book</span>(<span class="hljs-params">book_id: int</span>):</span>
     book = next((book <span class="hljs-keyword">for</span> book <span class="hljs-keyword">in</span> books <span class="hljs-keyword">if</span> book[<span class="hljs-string">"id"</span>] == book_id), <span class="hljs-literal">None</span>)
     <span class="hljs-keyword">if</span> <span class="hljs-keyword">not</span> book:
         <span class="hljs-keyword">raise</span> HTTPException(status_code=<span class="hljs-number">404</span>, detail=<span class="hljs-string">"Book not found"</span>)
     <span class="hljs-keyword">return</span> book
</code></pre>
</li>
<li><p>Save the file and test it locally using <code>uvicorn</code>:</p>
<pre><code class="lang-sh"> uvicorn main:app --reload
</code></pre>
</li>
<li><p>Open <a target="_blank" href="http://127.0.0.1:8000/docs"><code>http://127.0.0.1:8000/docs</code></a> to test the new endpoint.</p>
</li>
</ol>
<hr />
<h2 id="heading-step-3-set-up-the-ci-pipeline"><strong>Step 3: Set Up the CI Pipeline</strong></h2>
<p>A CI pipeline will run tests automatically.</p>
<h3 id="heading-create-githubworkflowsciyml"><strong>Create</strong> <code>.github/workflows/ci.yml</code></h3>
<ol>
<li><p>Create a <code>.github/workflows/ci.yml</code> file with:</p>
<pre><code class="lang-yaml"> <span class="hljs-attr">name:</span> <span class="hljs-string">CI</span>

 <span class="hljs-attr">on:</span>
   <span class="hljs-attr">pull_request:</span>
     <span class="hljs-attr">branches:</span>
       <span class="hljs-bullet">-</span> <span class="hljs-string">main</span>

 <span class="hljs-attr">jobs:</span>
   <span class="hljs-attr">test:</span>
     <span class="hljs-attr">runs-on:</span> <span class="hljs-string">ubuntu-latest</span>
     <span class="hljs-attr">steps:</span>
       <span class="hljs-bullet">-</span> <span class="hljs-attr">name:</span> <span class="hljs-string">Checkout</span> <span class="hljs-string">repository</span>
         <span class="hljs-attr">uses:</span> <span class="hljs-string">actions/checkout@v3</span>
       <span class="hljs-bullet">-</span> <span class="hljs-attr">name:</span> <span class="hljs-string">Set</span> <span class="hljs-string">up</span> <span class="hljs-string">Python</span>
         <span class="hljs-attr">uses:</span> <span class="hljs-string">actions/setup-python@v3</span>
         <span class="hljs-attr">with:</span>
           <span class="hljs-attr">python-version:</span> <span class="hljs-string">"3.9"</span>
       <span class="hljs-bullet">-</span> <span class="hljs-attr">name:</span> <span class="hljs-string">Install</span> <span class="hljs-string">dependencies</span>
         <span class="hljs-attr">run:</span> <span class="hljs-string">|
           pip install -r requirements.txt
</span>       <span class="hljs-bullet">-</span> <span class="hljs-attr">name:</span> <span class="hljs-string">Run</span> <span class="hljs-string">tests</span>
         <span class="hljs-attr">run:</span> <span class="hljs-string">pytest</span>
</code></pre>
</li>
<li><p>Commit and push this file.</p>
</li>
<li><p>Open a <strong>Pull Request</strong> and verify the test job runs successfully.</p>
</li>
</ol>
<hr />
<h2 id="heading-step-4-set-up-the-deployment-pipeline"><strong>Step 4: Set Up the Deployment Pipeline</strong></h2>
<h3 id="heading-create-githubworkflowsdeployyml"><strong>Create</strong> <code>.github/workflows/deploy.yml</code></h3>
<ol>
<li><p>Add a new file <code>.github/workflows/deploy.yml</code>:</p>
<pre><code class="lang-yaml"> <span class="hljs-attr">name:</span> <span class="hljs-string">Deploy</span>

 <span class="hljs-attr">on:</span>
   <span class="hljs-attr">push:</span>
     <span class="hljs-attr">branches:</span>
       <span class="hljs-bullet">-</span> <span class="hljs-string">main</span>

 <span class="hljs-attr">jobs:</span>
   <span class="hljs-attr">deploy:</span>
     <span class="hljs-attr">runs-on:</span> <span class="hljs-string">ubuntu-latest</span>
     <span class="hljs-attr">steps:</span>
       <span class="hljs-bullet">-</span> <span class="hljs-attr">name:</span> <span class="hljs-string">Checkout</span> <span class="hljs-string">repository</span>
         <span class="hljs-attr">uses:</span> <span class="hljs-string">actions/checkout@v3</span>
       <span class="hljs-bullet">-</span> <span class="hljs-attr">name:</span> <span class="hljs-string">Build</span> <span class="hljs-string">and</span> <span class="hljs-string">Push</span> <span class="hljs-string">Docker</span> <span class="hljs-string">Image</span>
         <span class="hljs-attr">run:</span> <span class="hljs-string">|
           docker build -t my-fastapi-app .
           docker run -d -p 8000:8000 my-fastapi-app</span>
</code></pre>
</li>
<li><p>Push changes and merge a PR to test deployment.</p>
</li>
</ol>
<hr />
<h2 id="heading-step-5-dockerize-the-fastapi-app"><strong>Step 5: Dockerize the FastAPI App</strong></h2>
<h3 id="heading-create-dockerfile"><strong>Create</strong> <code>Dockerfile</code></h3>
<pre><code class="lang-dockerfile"><span class="hljs-keyword">FROM</span> python:<span class="hljs-number">3.9</span>-slim
<span class="hljs-keyword">WORKDIR</span><span class="bash"> /app</span>
<span class="hljs-keyword">COPY</span><span class="bash"> requirements.txt ./</span>
<span class="hljs-keyword">RUN</span><span class="bash"> pip install --no-cache-dir -r requirements.txt</span>
<span class="hljs-keyword">COPY</span><span class="bash"> . .</span>
<span class="hljs-keyword">CMD</span><span class="bash"> [<span class="hljs-string">"uvicorn"</span>, <span class="hljs-string">"main:app"</span>, <span class="hljs-string">"--host"</span>, <span class="hljs-string">"0.0.0.0"</span>, <span class="hljs-string">"--port"</span>, <span class="hljs-string">"8000"</span>]</span>
</code></pre>
<ol>
<li><p>Build and test locally:</p>
<pre><code class="lang-sh"> docker build -t fastapi-app .
 docker run -p 8000:8000 fastapi-app
</code></pre>
</li>
</ol>
<hr />
<h2 id="heading-step-6-set-up-nginx-as-a-reverse-proxy"><strong>Step 6: Set Up Nginx as a Reverse Proxy</strong></h2>
<h3 id="heading-modify-nginxconf"><strong>Modify</strong> <code>nginx.conf</code></h3>
<ol>
<li><p>Create <code>nginx.conf</code>:</p>
<pre><code class="lang-nginx"> <span class="hljs-section">server</span> {
     <span class="hljs-attribute">listen</span> <span class="hljs-number">80</span>;
     <span class="hljs-attribute">server_name</span> localhost;

     <span class="hljs-attribute">location</span> / {
         <span class="hljs-attribute">proxy_pass</span> http://127.0.0.1:8000;
         <span class="hljs-attribute">proxy_set_header</span> Host <span class="hljs-variable">$host</span>;
         <span class="hljs-attribute">proxy_set_header</span> X-Real-IP <span class="hljs-variable">$remote_addr</span>;
     }
 }
</code></pre>
</li>
<li><p>Run Nginx with Docker:</p>
<pre><code class="lang-sh"> docker run --name nginx -v $(<span class="hljs-built_in">pwd</span>)/nginx.conf:/etc/nginx/nginx.conf:ro -p 80:80 nginx
</code></pre>
</li>
</ol>
<hr />
<h2 id="heading-step-7-deploy-the-application"><strong>Step 7: Deploy the Application</strong></h2>
<ol>
<li><p>Push the Docker image to a registry (e.g., Docker Hub, AWS ECR, GitHub Container Registry).</p>
<pre><code class="lang-sh"> docker tag fastapi-app your-username/fastapi-app:latest
 docker push your-username/fastapi-app:latest
</code></pre>
</li>
<li><p>Deploy to a cloud provider or a self-hosted server with Docker and Nginx configured.</p>
</li>
</ol>
<hr />
<h2 id="heading-conclusion"><strong>Conclusion</strong></h2>
<p>This project strengthened my <strong>CI/CD automation</strong>, <strong>Dockerization</strong>, and <strong>Nginx deployment</strong> skills.</p>
<p>I successfully:</p>
<ol>
<li><p>Implemented a missing API endpoint</p>
</li>
<li><p>Set up a deployment pipeline.</p>
</li>
<li><p>Configured Nginx as a reverse proxy.</p>
</li>
</ol>
<p>With each project I complete at HNG I learn a lot and I look forward to the next ones</p>
<p>#DevOps #FastAPI #Docker #GitHubActions #CI/CD #Nginx #CloudComputing</p>
]]></content:encoded></item><item><title><![CDATA[HNG DevOps - Stage 1]]></title><description><![CDATA[Introduction
As part of my HNG internship, I built a Number Classification API that takes an integer and returns its mathematical properties along with a fun fact. This project tested my API development, deployment, and troubleshooting skills.
In thi...]]></description><link>https://blog.victoriliya.com/hng-devops-stage-1</link><guid isPermaLink="true">https://blog.victoriliya.com/hng-devops-stage-1</guid><category><![CDATA[Hnginternship]]></category><category><![CDATA[Devops]]></category><category><![CDATA[Devops articles]]></category><dc:creator><![CDATA[VICTOR ILIYA]]></dc:creator><pubDate>Thu, 20 Feb 2025 03:05:01 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1740020407304/442a281e-6e4d-4f61-a7c8-a69074f68a23.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<h1 id="heading-introduction">Introduction</h1>
<p>As part of my HNG internship, I built a <strong>Number Classification API</strong> that takes an integer and returns its mathematical properties along with a fun fact. This project tested my API development, deployment, and troubleshooting skills.</p>
<p>In this post, I'll walk you through the entire process, from setting up the Flask API to deploying it on Render.</p>
<hr />
<h2 id="heading-1-setting-up-the-project">1️⃣ Setting Up the Project</h2>
<h3 id="heading-step-1-install-python-and-flask">Step 1: Install Python and Flask</h3>
<p>First, ensure you have Python installed. If not, download it from <a target="_blank" href="https://www.python.org/">python.org</a>.</p>
<p>Then, create a project directory and set up a virtual environment:</p>
<pre><code class="lang-sh">mkdir number-classification-api &amp;&amp; <span class="hljs-built_in">cd</span> number-classification-api
python -m venv venv
<span class="hljs-built_in">source</span> venv/bin/activate  <span class="hljs-comment"># On Windows, use venv\Scripts\activate</span>
</code></pre>
<p>Now, install Flask:</p>
<pre><code class="lang-sh">pip install flask requests
</code></pre>
<hr />
<h2 id="heading-2-developing-the-api">2️⃣ Developing the API</h2>
<h3 id="heading-step-2-create-the-flask-application">Step 2: Create the Flask Application</h3>
<p>Inside your project directory, create a file called <code>app.py</code> and add the following code:</p>
<pre><code class="lang-python"><span class="hljs-keyword">import</span> os
<span class="hljs-keyword">import</span> requests
<span class="hljs-keyword">from</span> flask <span class="hljs-keyword">import</span> Flask, request, jsonify

<span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">is_prime</span>(<span class="hljs-params">n</span>):</span>
    <span class="hljs-keyword">if</span> n &lt; <span class="hljs-number">2</span>:
        <span class="hljs-keyword">return</span> <span class="hljs-literal">False</span>
    <span class="hljs-keyword">for</span> i <span class="hljs-keyword">in</span> range(<span class="hljs-number">2</span>, int(n ** <span class="hljs-number">0.5</span>) + <span class="hljs-number">1</span>):
        <span class="hljs-keyword">if</span> n % i == <span class="hljs-number">0</span>:
            <span class="hljs-keyword">return</span> <span class="hljs-literal">False</span>
    <span class="hljs-keyword">return</span> <span class="hljs-literal">True</span>

<span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">is_armstrong</span>(<span class="hljs-params">n</span>):</span>
    digits = [int(d) <span class="hljs-keyword">for</span> d <span class="hljs-keyword">in</span> str(n)]
    <span class="hljs-keyword">return</span> sum(d ** len(digits) <span class="hljs-keyword">for</span> d <span class="hljs-keyword">in</span> digits) == n

<span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">classify_number</span>(<span class="hljs-params">n</span>):</span>
    properties = []
    <span class="hljs-keyword">if</span> is_armstrong(n):
        properties.append(<span class="hljs-string">"armstrong"</span>)
    properties.append(<span class="hljs-string">"even"</span> <span class="hljs-keyword">if</span> n % <span class="hljs-number">2</span> == <span class="hljs-number">0</span> <span class="hljs-keyword">else</span> <span class="hljs-string">"odd"</span>)
    <span class="hljs-keyword">return</span> properties

<span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">get_fun_fact</span>(<span class="hljs-params">n</span>):</span>
    response = requests.get(<span class="hljs-string">f"http://numbersapi.com/<span class="hljs-subst">{n}</span>/math"</span>)
    <span class="hljs-keyword">return</span> response.text <span class="hljs-keyword">if</span> response.status_code == <span class="hljs-number">200</span> <span class="hljs-keyword">else</span> <span class="hljs-string">"No fact available."</span>

app = Flask(__name__)

<span class="hljs-meta">@app.route("/api/classify-number")</span>
<span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">classify</span>():</span>
    number = request.args.get(<span class="hljs-string">"number"</span>)
    <span class="hljs-keyword">if</span> <span class="hljs-keyword">not</span> number <span class="hljs-keyword">or</span> <span class="hljs-keyword">not</span> number.isdigit():
        <span class="hljs-keyword">return</span> jsonify({<span class="hljs-string">"number"</span>: number, <span class="hljs-string">"error"</span>: <span class="hljs-literal">True</span>}), <span class="hljs-number">400</span>

    number = int(number)
    <span class="hljs-keyword">return</span> jsonify({
        <span class="hljs-string">"number"</span>: number,
        <span class="hljs-string">"is_prime"</span>: is_prime(number),
        <span class="hljs-string">"is_perfect"</span>: <span class="hljs-literal">False</span>,  <span class="hljs-comment"># Placeholder for perfection check</span>
        <span class="hljs-string">"properties"</span>: classify_number(number),
        <span class="hljs-string">"digit_sum"</span>: sum(int(d) <span class="hljs-keyword">for</span> d <span class="hljs-keyword">in</span> str(number)),
        <span class="hljs-string">"fun_fact"</span>: get_fun_fact(number)
    })

<span class="hljs-keyword">if</span> __name__ == <span class="hljs-string">"__main__"</span>:
    port = int(os.environ.get(<span class="hljs-string">"PORT"</span>, <span class="hljs-number">5000</span>))
    app.run(host=<span class="hljs-string">"0.0.0.0"</span>, port=port)
</code></pre>
<hr />
<h2 id="heading-3-testing-the-api-locally">3️⃣ Testing the API Locally</h2>
<h3 id="heading-step-3-run-the-flask-server">Step 3: Run the Flask Server</h3>
<p>Start the server by running:</p>
<pre><code class="lang-sh">python app.py
</code></pre>
<p>Visit the following URL in your browser:</p>
<pre><code class="lang-bash">http://127.0.0.1:5000/api/classify-number?number=371
</code></pre>
<p>Expected response:</p>
<pre><code class="lang-json">{
    <span class="hljs-attr">"number"</span>: <span class="hljs-number">371</span>,
    <span class="hljs-attr">"is_prime"</span>: <span class="hljs-literal">false</span>,
    <span class="hljs-attr">"is_perfect"</span>: <span class="hljs-literal">false</span>,
    <span class="hljs-attr">"properties"</span>: [<span class="hljs-string">"armstrong"</span>, <span class="hljs-string">"odd"</span>],
    <span class="hljs-attr">"digit_sum"</span>: <span class="hljs-number">11</span>,
    <span class="hljs-attr">"fun_fact"</span>: <span class="hljs-string">"371 is an Armstrong number because 3^3 + 7^3 + 1^3 = 371"</span>
}
</code></pre>
<hr />
<h2 id="heading-4-deploying-to-render">4️⃣ Deploying to Render</h2>
<h3 id="heading-step-4-prepare-for-deployment">Step 4: Prepare for Deployment</h3>
<p>Create a <code>requirements.txt</code> file:</p>
<pre><code class="lang-sh">pip freeze &gt; requirements.txt
</code></pre>
<p>Also, create a <code>Procfile</code> (without any file extension) and add the following line:</p>
<pre><code class="lang-bash">web: python app.py
</code></pre>
<h3 id="heading-step-5-push-code-to-github">Step 5: Push Code to GitHub</h3>
<p>Initialize a Git repository and push the project:</p>
<pre><code class="lang-sh">git init
git add .
git commit -m <span class="hljs-string">"Initial commit"</span>
git branch -M main
git remote add origin &lt;your-github-repo-url&gt;
git push -u origin main
</code></pre>
<h3 id="heading-step-6-deploy-on-render">Step 6: Deploy on Render</h3>
<ol>
<li><p>Log in to <a target="_blank" href="https://render.com/">Render</a> and create a new <strong>Web Service</strong>.</p>
</li>
<li><p>Connect your GitHub repository.</p>
</li>
<li><p>Set the <strong>start command</strong> as:</p>
</li>
</ol>
<pre><code class="lang-sh">python app.py
</code></pre>
<ol start="4">
<li>Click <strong>Deploy</strong>.</li>
</ol>
<h3 id="heading-step-7-access-the-deployed-api">Step 7: Access the Deployed API</h3>
<p>Once deployment is successful, Render provides a public URL, e.g.,</p>
<pre><code class="lang-bash">https://your-app-name.onrender.com/api/classify-number?number=371
</code></pre>
<p>Now, you can access your API from anywhere! 🎉</p>
<hr />
<h2 id="heading-5-conclusion">5️⃣ Conclusion</h2>
<p>This project helped me:</p>
<p>✅ Build and structure a Flask API.<br />✅ Implement mathematical logic in Python.<br />✅ Deploy an API on Render and troubleshoot issues.</p>
<p>Got questions? Drop them in the comments! 🚀</p>
<p>#DevOps #Python #Flask #APIDevelopment #CloudComputing #Render #Internship #LearningByDoing</p>
]]></content:encoded></item><item><title><![CDATA[HNG DevOps - Stage 0]]></title><description><![CDATA[Introduction
Starting my HNG Internship, one of the first tasks assigned to DevOps interns was to set up NGINX on Ubuntu and configure it to serve a custom web page. This task aimed to evaluate our ability to install, configure, and troubleshoot a we...]]></description><link>https://blog.victoriliya.com/hng-devops-stage-0</link><guid isPermaLink="true">https://blog.victoriliya.com/hng-devops-stage-0</guid><category><![CDATA[Hnginternship]]></category><category><![CDATA[nginx]]></category><category><![CDATA[ec2]]></category><category><![CDATA[hng12]]></category><dc:creator><![CDATA[VICTOR ILIYA]]></dc:creator><pubDate>Sat, 15 Feb 2025 07:06:54 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1739602501193/27be8476-b2cb-427d-bd21-f4f8a38576fd.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<h2 id="heading-introduction">Introduction</h2>
<p>Starting my HNG Internship, one of the first tasks assigned to DevOps interns was to set up NGINX on Ubuntu and configure it to serve a custom web page. This task aimed to evaluate our ability to install, configure, and troubleshoot a web server from scratch. In this blog post, I’ll walk you through how I approached and completed this task, including any challenges I faced and the key lessons I learned.</p>
<hr />
<h2 id="heading-task-overview">Task Overview</h2>
<p>The task required me to:</p>
<ol>
<li><p>Install the NGINX web server and ensure it is running.</p>
</li>
<li><p>Configure NGINX to serve a custom HTML page as the default page with the message:</p>
</li>
</ol>
<pre><code class="lang-bash">Welcome to DevOps Stage 0 - Victor Iliya/Non-Existent
</code></pre>
<ol start="3">
<li>Write a blog post that documents your experience with this task.</li>
</ol>
<h2 id="heading-step-by-step-guide">Step-by-Step Guide</h2>
<h3 id="heading-install-nginx">Install NGINX</h3>
<ol>
<li><p><strong>Created the Web Serve</strong>r:</p>
<p> The first thing I did was to create an EC2 instance on AWS to be my web server. I used an ubuntu AMI.</p>
</li>
</ol>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1739605365349/965ea950-9003-48bb-9c69-32d50011aa95.png" alt class="image--center mx-auto" /></p>
<ol start="2">
<li><p><strong>Connect to the instance</strong></p>
<p> There are many ways to connect to an EC2 instance, I used the EC2 instance connect.</p>
</li>
</ol>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1739605333296/9c14b739-400b-482b-a495-e44114bfa03e.png" alt class="image--center mx-auto" /></p>
<ol start="3">
<li><p><strong>Update Ubuntu</strong></p>
<p> Once connected to the EC2 instance, the first thing t do it to update the Linux server by running the code below.</p>
</li>
</ol>
<pre><code class="lang-bash">sudo apt update
</code></pre>
<ol start="4">
<li><p><strong>Install NGINX</strong></p>
<p> The next step is to install the NGINX service</p>
</li>
</ol>
<pre><code class="lang-bash">sudo apt install nginx -y
</code></pre>
<ol start="5">
<li><p><strong>Enable and start NGINX</strong></p>
<p> After installation, I enabled and started the NGINX service:</p>
</li>
</ol>
<pre><code class="lang-bash">sudo systemctl <span class="hljs-built_in">enable</span> nginx
sudo systemctl start nginx
</code></pre>
<ol start="6">
<li><strong>Confirm if NGINX is running</strong></li>
</ol>
<pre><code class="lang-bash">systemctl status nginx
</code></pre>
<p>If everything worked correctly, you should have the following:</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1739606304878/3f775358-8da0-4f09-a49e-564210ccf79a.png" alt class="image--center mx-auto" /></p>
<h3 id="heading-configure-the-default-web-page">Configure the Default Web Page</h3>
<ol>
<li><p><strong>Modify the default index.html page:</strong></p>
<p> For the server to display the page, you need to modify the default index.html page located at /vat/www/html/index.html.</p>
<p> Use the vim command for this as such:</p>
</li>
</ol>
<pre><code class="lang-bash">vim /var/www/html/index.html
</code></pre>
<p>Now, to modify text files with vim, press the “ i “ key on the keyboard which is for insert, and now you’d be able to enter the text you want, in my case it would be “Welcome to DevOps Stage 0 - Victor Iliya/ Non-Existent“</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1739608044916/3aa0313b-a3a5-4a66-9f57-5e259aa579bc.png" alt class="image--center mx-auto" /></p>
<p>Now to save the file, perform the following:</p>
<ul>
<li><p>press the “ esc “ key to exit the insert mode.</p>
</li>
<li><p>type <code>:wq!</code> and press the enter key to save the file and close the vim editor.</p>
</li>
</ul>
<p>Note: If the permission is denied, use sudo as such:</p>
<pre><code class="lang-bash">sudo vim /var/www/html/index.html
</code></pre>
<ol start="2">
<li><p><strong>Restart the NGINX server</strong></p>
<p> Restart the NGINX server to apply the changes</p>
<pre><code class="lang-bash"> sudo systemctl restart nginx
</code></pre>
</li>
</ol>
<h3 id="heading-open-your-web-page">Open your web page</h3>
<p>Go back to your instance and find your public ip address, use this to access the site you’ve just made.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1739609091403/7d5db421-985e-44dc-8f6e-e9f4369e4797.png" alt class="image--center mx-auto" /></p>
<p><strong>Note: Ensure the security groups for your instance allows HTTP traffic on port 80</strong></p>
<ol>
<li><p>Hands-on experience installing and configuring NGINX</p>
</li>
<li><p>Debugging and troubleshooting web server issues</p>
</li>
<li><p>Improved Linux server management skills</p>
</li>
</ol>
<h3 id="heading-references">References</h3>
<p>I explored different engineering roles at HNG for future career growth:</p>
<ul>
<li><p><a target="_blank" href="https://hng.tech/hire/devops-engineers">DevOps Engineers</a></p>
</li>
<li><p><a target="_blank" href="https://hng.tech/hire/cloud-engineers">cloud Engineers</a></p>
</li>
</ul>
<p>This was a great learning experience, and I’m excited for what’s next!</p>
]]></content:encoded></item><item><title><![CDATA[Intrusion Detection System (IDS)]]></title><description><![CDATA[Introduction
This project is an Intrusion Detection System (IDS) that allows users to upload network data files for Machine Learning (ML) inference. The system pre-processes the uploaded file, runs it through an ML model trained on the NSL-KDD datase...]]></description><link>https://blog.victoriliya.com/intrusion-detection-system</link><guid isPermaLink="true">https://blog.victoriliya.com/intrusion-detection-system</guid><category><![CDATA[AWS]]></category><category><![CDATA[Devops]]></category><category><![CDATA[Python]]></category><category><![CDATA[ci-cd]]></category><category><![CDATA[Developer]]></category><category><![CDATA[Docker]]></category><dc:creator><![CDATA[VICTOR ILIYA]]></dc:creator><pubDate>Mon, 13 Jan 2025 10:31:40 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1736775990762/87035794-9f90-49d0-a1dd-5c1930e315d5.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<h2 id="heading-introduction"><strong>Introduction</strong></h2>
<p>This project is an <strong>Intrusion Detection System (IDS)</strong> that allows users to upload network data files for Machine Learning (ML) inference. The system pre-processes the uploaded file, runs it through an ML model trained on the NSL-KDD dataset, and returns the classification results to the frontend. Processed results are stored in <strong>S3</strong>, and job status is tracked in <strong>DynamoDB</strong>.</p>
<p>The architecture is built using a <strong>Microservices architecture</strong> hosted on <strong>AWS Fargate (ECS)</strong> with an internal and external load balancer setup, ensuring secure and scalable operations.</p>
<hr />
<h2 id="heading-objectives"><strong>Objectives</strong></h2>
<p>The primary goals of the IDS project are:</p>
<ul>
<li><p><strong>Data Pre-processing</strong>: Transform raw network traffic data (based on the NSL-KDD dataset) into a structured format for machine learning.</p>
</li>
<li><p><strong>Model Training</strong>: Compare multiple machine learning models to identify the most accurate and efficient one for intrusion detection.</p>
</li>
<li><p><strong>User Interaction</strong>: Build a user-friendly web interface using Flask for users to upload network data for classification.</p>
</li>
<li><p><strong>File Classification</strong>: Build the application that will classify the uploaded file based on the machine learning model.</p>
</li>
<li><p><strong>Cloud Deployment</strong>: Deploy the entire system using <strong>AWS ECS Fargate</strong>, <strong>Terraform</strong>, and a robust CI/CD pipeline for automation.</p>
</li>
<li><p><strong>Scalability</strong>: Ensure the system can handle increased traffic and adapt to evolving threats.</p>
</li>
<li><p><strong>CI/CD Pipeline:</strong> Create a CI/CD pipeline that’ll ensure seamless deployment of code changes.</p>
</li>
</ul>
<hr />
<h3 id="heading-tech-stack">🛠️ <strong>Tech Stack</strong></h3>
<ol>
<li><p><strong>Machine Learning</strong>: Pre-processing with Pandas and NumpPy, and training models with SciKit-learn.</p>
</li>
<li><p><strong>Frontend</strong>: NextJS frontend</p>
</li>
<li><p><strong>Backend</strong>: Flask-based web application for user interaction.</p>
</li>
<li><p><strong>Infrastructure as Code (IaC)</strong>: Terraform for provisioning AWS resources.</p>
</li>
<li><p><strong>CI/CD</strong>: Automated deployment pipeline (To Be Determined).</p>
</li>
<li><p><strong>Cloud Deployment</strong>: Containerized application.</p>
</li>
<li><p><strong>Visualization</strong>: Generate real-time classification insights with Matplotlib.</p>
</li>
</ol>
<hr />
<h3 id="heading-key-features">🚦 <strong>Key Features</strong></h3>
<ul>
<li><p><strong>Efficient Data Processing</strong>: Leverages advanced preprocessing techniques to prepare data for model training.</p>
</li>
<li><p><strong>Multi-Model Comparison</strong>: Tested models such as Random Forest, Decision Tree, Naive Bayes, and Logistic Regression to ensure the best performance.</p>
</li>
<li><p><strong>Interactive User Interface</strong>: Upload network traffic data and receive immediate threat classification.</p>
</li>
<li><p><strong>Cloud-Native Architecture</strong>: Deployed using AWS services for high availability and scalability.</p>
</li>
</ul>
<hr />
<h3 id="heading-project-workflow">🔍 <strong>Project Workflow</strong></h3>
<ol>
<li><p><strong>Data Preprocessing</strong>: Cleaned and transformed the NSL-KDD dataset into machine-readable formats.</p>
</li>
<li><p><strong>Model Training</strong>: Trained multiple models to classify threats, comparing metrics like <strong>accuracy</strong>, <strong>precision</strong>, <strong>recall</strong>, and <strong>F1 score</strong> to select the best model.</p>
</li>
<li><p><strong>Model Deployment</strong>: Packaged the trained model as a Docker container and pushed it to Amazon ECR.</p>
</li>
<li><p><strong>Web App Development</strong>: Created a Flask-based app for user interactions.</p>
</li>
<li><p><strong>Cloud Deployment</strong>: Deployed the app on AWS ECS Fargate, ensuring security and scalability.</p>
</li>
</ol>
<hr />
<h3 id="heading-whats-next">🏆 <strong>What’s Next?</strong></h3>
<p>This is just the beginning! In future posts, I’ll dive deeper into:</p>
<ul>
<li><p>Enhancing the dataset by incorporating real-world scenarios.</p>
</li>
<li><p>Improving the Flask app with user authentication and a database for result tracking.</p>
</li>
<li><p>Scaling the system with Kubernetes for even greater flexibility.</p>
</li>
<li><p>Exploring real-time anomaly detection using advanced ML techniques.</p>
</li>
</ul>
<hr />
<h3 id="heading-stay-tuned">🔗 <strong>Stay Tuned!</strong></h3>
<p>This post is part of my <strong>Projects</strong> series. For all updates and technical details on this Intrusion Detection System, check out my <a target="_blank" href="https://victoriliya.hashnode.dev/series/intrusion-detection-system"><strong>Intrusion Detection System</strong></a> series, where I'll break down each component and share insights along the way.</p>
<p>Thank you for following my journey! Feel free to share your thoughts or ask questions in the comments. 😊</p>
]]></content:encoded></item><item><title><![CDATA[Intrusion Detection System: Documentation]]></title><description><![CDATA[Intrusion Detection System (IDS) Project Requirements
Overview
The Intrusion Detection System (IDS) project aims to provide an efficient and scalable solution for detecting malicious network traffic patterns. It utilizes machine learning models train...]]></description><link>https://blog.victoriliya.com/intrusion-detection-system-documentation</link><guid isPermaLink="true">https://blog.victoriliya.com/intrusion-detection-system-documentation</guid><category><![CDATA[Intrusion detection system]]></category><category><![CDATA[Devops]]></category><category><![CDATA[Devops articles]]></category><category><![CDATA[AWS]]></category><category><![CDATA[Python]]></category><dc:creator><![CDATA[VICTOR ILIYA]]></dc:creator><pubDate>Thu, 09 Jan 2025 22:14:08 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1737384069847/163098a5-7765-48a9-aba4-0a218e5ec36f.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<h1 id="heading-intrusion-detection-system-ids-project-requirements">Intrusion Detection System (IDS) Project Requirements</h1>
<h2 id="heading-overview">Overview</h2>
<p>The Intrusion Detection System (IDS) project aims to provide an efficient and scalable solution for detecting malicious network traffic patterns. It utilizes machine learning models trained on the NSL-KDD dataset and provides a user-friendly interface for visualization and analysis. The system includes a backend for processing data, generating predictions, and creating visual summaries, along with a frontend for user interaction.</p>
<hr />
<h2 id="heading-functional-requirements">Functional Requirements</h2>
<h3 id="heading-1-data-processing">1. Data Processing</h3>
<ul>
<li><p><strong>Input:</strong></p>
<ul>
<li>Accept uploaded CSV files containing network traffic data in the NSL-KDD dataset format.</li>
</ul>
</li>
<li><p><strong>Preprocessing:</strong></p>
<ul>
<li><p>Handle missing or incorrect data gracefully.</p>
</li>
<li><p>Encode categorical features using one-hot encoding.</p>
</li>
<li><p>Scale numerical features using a pre-trained StandardScaler.</p>
</li>
</ul>
</li>
</ul>
<h3 id="heading-2-machine-learning-model">2. Machine Learning Model</h3>
<ul>
<li><p><strong>Model Details:</strong></p>
<ul>
<li>Load a pre-trained Random Forest model (<code>Random_Forest.joblib</code>) for classification.</li>
</ul>
</li>
<li><p><strong>Predictions:</strong></p>
<ul>
<li>Classify network traffic into the following categories: Normal, DoS, Probe, R2L, U2R.</li>
</ul>
</li>
</ul>
<h3 id="heading-3-visualization">3. Visualization</h3>
<ul>
<li><p><strong>Graphical Representation:</strong></p>
<ul>
<li><p>Display results in a bar graph with:</p>
<ul>
<li><p>X-axis labeled with traffic categories.</p>
</li>
<li><p>Y-axis representing the count of instances.</p>
</li>
</ul>
</li>
<li><p>Labels for aggregated categories: “Normal Traffic” and “Malicious Traffic.”</p>
</li>
</ul>
</li>
<li><p><strong>Summary Report:</strong></p>
<ul>
<li><p>Provide a numerical summary:</p>
<ul>
<li><p>Count of normal traffic.</p>
</li>
<li><p>Count of all malicious traffic (aggregated).</p>
</li>
</ul>
</li>
</ul>
</li>
</ul>
<h3 id="heading-4-backend">4. Backend</h3>
<ul>
<li><p><strong>APIs:</strong></p>
<ul>
<li><p><strong>/upload:</strong> Accepts file uploads and triggers processing.</p>
</li>
<li><p><strong>/process:</strong> Processes uploaded data, generates predictions, and creates visual output.</p>
</li>
</ul>
</li>
<li><p><strong>Error Handling:</strong></p>
<ul>
<li><p>Return appropriate error messages for:</p>
<ul>
<li><p>Missing files.</p>
</li>
<li><p>Model loading issues.</p>
</li>
<li><p>Invalid data format.</p>
</li>
</ul>
</li>
</ul>
</li>
</ul>
<h3 id="heading-5-frontend">5. Frontend</h3>
<ul>
<li><p><strong>User Interface:</strong></p>
<ul>
<li><p>Provide buttons to:</p>
<ul>
<li><p>Upload files.</p>
</li>
<li><p>Trigger data processing.</p>
</li>
</ul>
</li>
<li><p>Display the generated graph and summary report.</p>
</li>
</ul>
</li>
<li><p><strong>Interactivity:</strong></p>
<ul>
<li>Ensure responsive and dynamic updates without reloading the page.</li>
</ul>
</li>
</ul>
<hr />
<h2 id="heading-non-functional-requirements">Non-Functional Requirements</h2>
<h3 id="heading-1-performance">1. Performance</h3>
<ul>
<li><p><strong>Processing Time:</strong></p>
<ul>
<li>Ensure data processing and prediction are completed within 5 seconds for datasets up to 10,000 rows.</li>
</ul>
</li>
</ul>
<h3 id="heading-2-scalability">2. Scalability</h3>
<ul>
<li><p><strong>File Size:</strong></p>
<ul>
<li>Handle CSV files up to 50 MB.</li>
</ul>
</li>
<li><p><strong>Concurrent Users:</strong></p>
<ul>
<li>Support up to 100 simultaneous users.</li>
</ul>
</li>
</ul>
<h3 id="heading-3-security">3. Security</h3>
<ul>
<li><p><strong>Data Validation:</strong></p>
<ul>
<li>Validate uploaded files to prevent malicious data injections.</li>
</ul>
</li>
<li><p><strong>Model Security:</strong></p>
<ul>
<li>Protect model files from unauthorized access.</li>
</ul>
</li>
</ul>
<h3 id="heading-4-maintainability">4. Maintainability</h3>
<ul>
<li><p><strong>Code Modularity:</strong></p>
<ul>
<li>Separate concerns into distinct modules for preprocessing, model loading, and visualization.</li>
</ul>
</li>
</ul>
<hr />
<h2 id="heading-technical-requirements">Technical Requirements</h2>
<h3 id="heading-1-backend">1. Backend</h3>
<ul>
<li><p><strong>Programming Language:</strong> Python 3.9+</p>
</li>
<li><p><strong>Frameworks:</strong> Flask</p>
</li>
<li><p><strong>Dependencies:</strong></p>
<ul>
<li><p>pandas</p>
</li>
<li><p>numpy</p>
</li>
<li><p>matplotlib</p>
</li>
<li><p>scikit-learn</p>
</li>
<li><p>joblib</p>
</li>
</ul>
</li>
</ul>
<h3 id="heading-2-frontend">2. Frontend</h3>
<ul>
<li><p><strong>Framework:</strong> Next.js</p>
</li>
<li><p><strong>Language:</strong> TypeScript</p>
</li>
<li><p><strong>CSS:</strong> Tailwind CSS</p>
</li>
</ul>
<h3 id="heading-3-deployment">3. Deployment</h3>
<ul>
<li><p><strong>Containerization:</strong> Docker</p>
</li>
<li><p><strong>Orchestration:</strong> Kubernetes</p>
</li>
<li><p><strong>Cloud:</strong> AWS (ECS Fargate, S3 for storage)</p>
</li>
</ul>
<h3 id="heading-4-infrastructure">4. Infrastructure</h3>
<ul>
<li><p><strong>Tools:</strong> Terraform for IaC</p>
</li>
<li><p><strong>Storage:</strong> Amazon S3 for temporary file storage</p>
</li>
</ul>
<hr />
<h2 id="heading-deliverables">Deliverables</h2>
<ol>
<li><p>Fully functional IDS application.</p>
</li>
<li><p>Dockerized backend and frontend services.</p>
</li>
<li><p>Kubernetes deployment manifests.</p>
</li>
<li><p>Documentation:</p>
<ul>
<li><p>User guide.</p>
</li>
<li><p>Deployment guide.</p>
</li>
<li><p>Developer guide.</p>
</li>
</ul>
</li>
</ol>
<hr />
<h2 id="heading-future-enhancements">Future Enhancements</h2>
<ol>
<li><p>Add real-time traffic monitoring.</p>
</li>
<li><p>Support additional data formats beyond NSL-KDD.</p>
</li>
<li><p>Integrate advanced visualization tools (e.g., D3.js).</p>
</li>
<li><p>Extend model to support additional attack categories.</p>
</li>
<li><p>Allow users to be able to sign up and save charts and results.</p>
</li>
<li><p>Allow users to use common type of network data</p>
</li>
</ol>
]]></content:encoded></item><item><title><![CDATA[Part 1: Setting Up Initial AWS Infrastructure for the Intrusion Detection System with Terraform]]></title><description><![CDATA[Introduction
This article is the first in a series where I’ll build an Intrusion Detection System(IDS) that classifies network traffic into normal and malicious traffic using AWS, Terraform, Flask, NextJS, and the other frameworks and tools.
In this ...]]></description><link>https://blog.victoriliya.com/inital-aws-infrastructure-terraform-setup</link><guid isPermaLink="true">https://blog.victoriliya.com/inital-aws-infrastructure-terraform-setup</guid><category><![CDATA[AWS]]></category><category><![CDATA[Terraform]]></category><dc:creator><![CDATA[VICTOR ILIYA]]></dc:creator><pubDate>Sat, 28 Dec 2024 18:21:19 GMT</pubDate><content:encoded><![CDATA[<h2 id="heading-introduction">Introduction</h2>
<p>This article is the first in a series where I’ll build an Intrusion Detection System(IDS) that classifies network traffic into normal and malicious traffic using AWS, Terraform, Flask, NextJS, and the other frameworks and tools.</p>
<p>In this part, I’ll focus on creating the foundational AWS infrastructure to manage the Terraform state files and the S3 bucket that will handle the uploaded dataset storage.</p>
<p><strong>NOTE: This is not a tutorial. The project summaries for each part will be posted here, the highlights, problems faced and their respective solutions.</strong></p>
<h3 id="heading-highlights-of-the-setup">Highlights of the setup</h3>
<p><strong>Terraform Backend</strong></p>
<p>This is where the state files of the main infrastructure will be stored. A separate remote backend ensures the project’s infrastructure management is seamless and collaborative, thus I provisioned:</p>
<ul>
<li><p>An S3 bucket for storing the Terraform state file</p>
</li>
<li><p>A DynamoDB table to lock the state file and prevent simultaneous conflicting operations.</p>
</li>
</ul>
<p>Although I did this alone and could have had the Terraform backend local, I believe having it remote on S3 is best practice.</p>
<p><strong>Terraform Main Infrastructure</strong></p>
<p>Now to the main infrastructure, with the backend already provisioned, this main infrastructure is set to use that backend. Both are separate, with the Backend independent and the Main dependent on the backend.</p>
<p>The first service I provisioned was an S3 bucket. This bucket will hold all the dataset uploads by the user and it will be versioned and encrypted.</p>
<p>An S3 bucket</p>
<h3 id="heading-verifying-the-setup">Verifying the setup</h3>
<p>Once I created my Terraform backend I went to my AWS management console to check the resources that have been made, and everything was. From the backend S3 Bucket and DynamoDB Table, to the main infrastructures S3 bucket, everything was provisioned accordingly.</p>
<h3 id="heading-challenges-faced">Challenges Faced</h3>
<p><strong>Backend and Main Infrastructure Together</strong>: Initially, I defined both the remote backend (S3 and DynamoDB) and the main infrastructure (e.g., S3 file storage bucket) within the same Terraform configuration. When I ran <code>terraform destroy</code> to clean up resources, the backend itself was deleted, preventing me from re-provisioning the infrastructure because there was no backend to manage the state file.</p>
<p>I resolved this by separating the backend setup into its own Terraform configuration, independent of the main infrastructure.</p>
<h3 id="heading-lessons-learned">Lessons Learned</h3>
<ol>
<li><p><strong>Separate Terraform configurations</strong>: I understood the importance of having the remote backend separate from the main infrastructure to prevent inadvertent deletions when i try to clean up my resources, ensuring my backend remains intact.</p>
</li>
<li><p><strong>Debugging Terraform:</strong> The <code>terraform validate</code> and <code>terraform plan</code> commands are invaluable for catching configuration errors early</p>
</li>
</ol>
<h2 id="heading-conclusion">Conclusion</h2>
<p>In this article, I outlined the initial setup of AWS infrastructure for our intrusion detection project. I configured a remote backend with S3 and DynamoDB and deployed an S3 bucket for file storage.</p>
<h3 id="heading-whats-next">What’s Next?</h3>
<p>In the next part, we’ll build the Next.js component that allows users to upload files to the S3 bucket via an API route in our Flask backend. Stay tuned!</p>
]]></content:encoded></item></channel></rss>