<?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" xmlns:media="http://search.yahoo.com/mrss/" version="2.0">
    <channel>
        
        <title>
            <![CDATA[ cybersecurity - freeCodeCamp.org ]]>
        </title>
        <description>
            <![CDATA[ Browse thousands of programming tutorials written by experts. Learn Web Development, Data Science, DevOps, Security, and get developer career advice. ]]>
        </description>
        <link>https://www.freecodecamp.org/news/</link>
        <image>
            <url>https://cdn.freecodecamp.org/universal/favicons/favicon.png</url>
            <title>
                <![CDATA[ cybersecurity - freeCodeCamp.org ]]>
            </title>
            <link>https://www.freecodecamp.org/news/</link>
        </image>
        <generator>Eleventy</generator>
        <lastBuildDate>Wed, 07 Oct 2026 09:33:32 +0000</lastBuildDate>
        <atom:link href="https://www.freecodecamp.org/news/tag/cybersecurity/rss.xml" rel="self" type="application/rss+xml" />
        <ttl>60</ttl>
        
            <item>
                <title>
                    <![CDATA[ How to Build an Endpoint Data Loss Prevention Strategy for Your Development Team ]]>
                </title>
                <description>
                    <![CDATA[ A developer's laptop holds more sensitive data than most people realize: API keys, database credentials, staging environment secrets, and sometimes entire copies of production data pulled down "just f ]]>
                </description>
                <link>https://www.freecodecamp.org/news/build-an-endpoint-data-loss-prevention-strategy-for-your-dev-team/</link>
                <guid isPermaLink="false">6aa9b28d96b8eb1b8b1475f0</guid>
                
                    <category>
                        <![CDATA[ data loss prevention ]]>
                    </category>
                
                    <category>
                        <![CDATA[ Data Protection ]]>
                    </category>
                
                    <category>
                        <![CDATA[ cybersecurity ]]>
                    </category>
                
                    <category>
                        <![CDATA[ CybersecurityAwareness ]]>
                    </category>
                
                    <category>
                        <![CDATA[ sensitive data ]]>
                    </category>
                
                    <category>
                        <![CDATA[ endpoint security ]]>
                    </category>
                
                    <category>
                        <![CDATA[ remote access ]]>
                    </category>
                
                    <category>
                        <![CDATA[ encryption ]]>
                    </category>
                
                    <category>
                        <![CDATA[ IT_Security ]]>
                    </category>
                
                    <category>
                        <![CDATA[ development ]]>
                    </category>
                
                    <category>
                        <![CDATA[ Web Development ]]>
                    </category>
                
                <dc:creator>
                    <![CDATA[ Alex Tray ]]>
                </dc:creator>
                <pubDate>Tue, 15 Sep 2026 21:03:09 +0000</pubDate>
                <media:content url="https://cdn.hashnode.com/uploads/covers/5e1e335a7a1d3fcc59028c64/4a1cf15f-7d3a-48d2-8fdd-b53ceb1dd0e7.png" medium="image" />
                <content:encoded>
                    <![CDATA[ <p>A developer's laptop holds more sensitive data than most people realize: API keys, database credentials, staging environment secrets, and sometimes entire copies of production data pulled down "just for testing."</p>
<p>End-users are responsible for <a href="https://www.mimecast.com/content/endpoint-dlp-data-loss-prevention/">75%</a> of internal data-loss incidents, most of them accidental rather than malicious. For a dev team, that risk concentrates on the endpoint, the machine where code gets written, tested, and pushed.</p>
<p>Here's how to build a strategy that protects that machine without slowing your team down.</p>
<h2 id="heading-table-of-contents">Table of Contents</h2>
<ul>
<li><p><a href="#heading-how-to-build-an-endpoint-data-loss-prevention-strategy">How to Build an Endpoint Data Loss Prevention Strategy</a></p>
<ul>
<li><p><a href="#heading-step-1-map-where-sensitive-data-lives-in-the-endpoint">Step 1: Map Where Sensitive Data Lives in the Endpoint</a></p>
</li>
<li><p><a href="#heading-step-2-set-access-control-as-the-foundation">Step 2: Set Access Control as the Foundation</a></p>
</li>
<li><p><a href="#heading-step-3-harden-the-endpoint-os-layer">Step 3: Harden the Endpoint OS Layer</a></p>
</li>
<li><p><a href="#heading-step-4-lock-down-containers-and-local-environments">Step 4: Lock Down Containers and Local Environments</a></p>
</li>
<li><p><a href="#heading-step-5-catch-leaks-before-they-ship">Step 5: Catch Leaks Before They Ship</a></p>
</li>
<li><p><a href="#heading-step-6-extend-coverage-to-public-facing-surfaces">Step 6: Extend Coverage to Public-Facing Surfaces</a></p>
</li>
<li><p><a href="#heading-step-7-monitor-measure-and-keep-policies-honest">Step 7: Monitor, Measure, and Keep Policies Honest</a></p>
</li>
</ul>
</li>
<li><p><a href="#heading-build-security-into-how-your-team-already-works">Build Security Into How Your Team Already Works</a></p>
</li>
</ul>
<h2 id="heading-how-to-build-an-endpoint-data-loss-prevention-strategy">How to Build an Endpoint Data Loss Prevention Strategy</h2>
<img src="https://cdn.hashnode.com/uploads/covers/65e715387099ff28d36bf4ac/f3675a55-3f39-4c4f-bb60-8c036791842e.png" alt="f3675a55-3f39-4c4f-bb60-8c036791842e" style="display: block;" width="746" height="415" loading="lazy">

<h3 id="heading-step-1-map-where-sensitive-data-lives-in-the-endpoint">Step 1: Map Where Sensitive Data Lives in the Endpoint</h3>
<p>Start by finding out where secrets and sensitive data sit across your team's machines. Likely candidates are things like config files with hardcoded credentials, .env files that never made it to <code>.gitignore</code>, and cached database dumps from a debugging session six months ago that nobody remembered to delete.</p>
<p>Anything that qualifies as a backup, even an ad hoc one, should be treated as sensitive data in its own right. So if the <a href="https://www.nakivo.com/blog/how-to-enable-backup-encryption/">backup encryption</a> isn't in place, that copy is just as vulnerable as the original source of data.</p>
<p>Most teams are surprised by what turns up once they start looking. A short audit across a handful of laptops usually reveals the same handful of habits repeating across a whole team, since one developer's shortcut tends to spread once it works one time.</p>
<p>A simple spreadsheet tracking what kind of sensitive data lives where and on which machines gives the rest of this strategy something concrete to build on. Skip this step and every control that follows ends up guessing at what it's supposed to be protecting.</p>
<p>Try this first:</p>
<ol>
<li><p>Choose three to five developer machines to begin your initial audit.</p>
</li>
<li><p>Search typical locations for environment files, credentials, database dumps, and private keys.</p>
</li>
<li><p>Record the finding, file location, data type, owner, and note if the data is still needed.</p>
</li>
<li><p>Remove unnecessary copies and update credentials that might have been exposed.</p>
</li>
</ol>
<p>For a Linux machine, a basic first pass could look like:</p>
<pre><code class="language-shell">find ~ -type f \( -name ".env" -o -name "*.pem" -o -name "*.key" \) 2&gt;/dev/null
</code></pre>
<p>This approach won't uncover every secret, but it gives your team a solid starting inventory.</p>
<p>For example, if the audit finds ~/projects/client-api/.env containing a database password, you need to move the credential to a secret manager, delete the local file, and update the password.</p>
<h3 id="heading-step-2-set-access-control-as-the-foundation">Step 2: Set Access Control as the Foundation</h3>
<p>Every endpoint DLP strategy sits on top of a working access control system. If every developer can pull production credentials regardless of their role, no amount of monitoring downstream will fix that gap.</p>
<p><a href="https://www.freecodecamp.org/news/how-to-build-scalable-access-control-for-your-web-app/">Scalable access control</a> that's built around roles and attributes limits what a compromised laptop can expose in the first place. After all, a stolen set of credentials only matters as much as the permissions attached to them.</p>
<p>This is also the cheapest control on this whole list to get wrong, and one of the easiest to fix.</p>
<p>Reviewing who has access to what on a recurring schedule rather than once at onboarding will catch the slow creep of permissions that no one remembered to revoke after a project ended.</p>
<p><strong>A simple implementation process:</strong></p>
<ol>
<li><p>List production systems and sensitive resources.</p>
</li>
<li><p>Create roles such as developer, senior developer, DevOps, and administrator.</p>
</li>
<li><p>Document which resources each role actually needs.</p>
</li>
<li><p>Remove permissions that don't support someone's current work.</p>
</li>
<li><p>Review access whenever someone changes projects or leaves the team.</p>
</li>
</ol>
<p>The difference between too much access and appropriate access is easier to see side by side. Here's the same developer role, granted two different ways in Postgres.</p>
<p><strong>Too much access:</strong></p>
<pre><code class="language-markdown">-- One role, every database, every table, every operation 

GRANT ALL PRIVILEGES ON DATABASE prod_db TO dev_team; 

GRANT ALL PRIVILEGES ON ALL TABLES IN SCHEMA public TO dev_team; 
</code></pre>
<p><strong>Appropriate access:</strong></p>
<pre><code class="language-markdown">-- Full access where the work actually happens 

GRANT CONNECT ON DATABASE staging_db TO dev_team; 

GRANT USAGE ON SCHEMA public TO dev_team; 

GRANT SELECT, INSERT, UPDATE, DELETE ON ALL TABLES IN SCHEMA public TO dev_team;
</code></pre>
<pre><code class="language-markdown">-- No standing connection to production at all 

REVOKE ALL ON DATABASE prod_db FROM dev_team; 
</code></pre>
<p>The same idea maps cleanly onto a role table, which is usually the easier version to hand to a team:</p>
<table style="width:629px"><colgroup><col style="width:78px"><col style="width:158px"><col style="width:141px"><col style="width:126px"><col style="width:126px"></colgroup><tbody><tr><th><p>Role</p></th><td><p>Dev / staging DB&nbsp;</p></td><td><p>Production DB</p></td><td><p>Secret Manager&nbsp;</p></td><td><p>Cloud Console</p></td></tr><tr><td><p>Developer</p></td><td><p>Read + write</p></td><td><p>None</p></td><td><p>Read, own project only</p></td><td><p>None</p></td></tr><tr><td><p>Senior Developer</p></td><td><p>Read + write</p></td><td><p>Read-only, time-limited</p></td><td><p>Read, own team's projects</p></td><td><p>Read-only&nbsp;</p></td></tr><tr><td><p>DevOps</p></td><td><p>Read + write</p></td><td><p>Write, scoped to deploys</p></td><td><p>Read + write&nbsp;</p></td><td><p>Admin, scoped&nbsp;</p></td></tr><tr><td><p>Administrator</p></td><td><p>Read + write</p></td><td><p>Full</p></td><td><p>Full</p></td><td><p>Full</p></td></tr></tbody></table>

<p>For example, a developer should have read and write access to the development and staging databases since those environments are part of their regular work. They shouldn't have direct access to the production database. A DevOps team member may need controlled production access for deployment and troubleshooting, with that access limited to the tasks they're responsible for.</p>
<h3 id="heading-step-3-harden-the-endpoint-os-layer">Step 3: Harden the Endpoint OS Layer</h3>
<p>Most development machines run some flavor of Linux, whether directly or through WSL, and the operating system layer is where a lot of DLP controls end up getting enforced day to day: file permissions, disk encryption, and keeping user privileges properly separated.</p>
<p>Getting comfortable with <a href="https://www.freecodecamp.org/news/the-linux-commands-handbook/">core Linux commands</a> makes it a lot easier to audit what's running on a machine, lock file permissions down the right way, and catch something out of place before it turns into a real problem.</p>
<p>Disk encryption also deserves a mention here. Steal a laptop with an unencrypted drive, and everything on it just hands itself over, no password needed, the moment someone pulls the disk and mounts it somewhere else.</p>
<p>Turn on full-disk encryption, and that same stolen laptop becomes a much smaller headache.</p>
<p>For Linux developers, here are a few commands you can run during an endpoint review:</p>
<pre><code class="language-markdown">whoami 
</code></pre>
<pre><code class="language-markdown">sudo -l 
</code></pre>
<pre><code class="language-markdown">ls -la ~/.ssh 
</code></pre>
<pre><code class="language-markdown">df -h 
</code></pre>
<p>These commands can help identify the current user, available sudo privileges, SSH files, and disk usage.</p>
<p>File permissions are where this gets specific. A secret sitting in a world-readable file is available to every process and every account on that machine, which quietly cancels out the access control work from the previous step.</p>
<p>Check what the permissions actually are before changing anything:</p>
<pre><code class="language-markdown">ls -la ~/.ssh
</code></pre>
<pre><code class="language-markdown">find ~/projects -name ".env" -exec ls -l {} \;
</code></pre>
<p>Output worth acting on looks like this:</p>
<pre><code class="language-markdown">-rw-r--r--  1 dev  staff  1704  Mar 12 09:14 /home/dev/.ssh/id_ed25519
</code></pre>
<pre><code class="language-markdown">-rw-rw-r--  1 dev  staff   612  Mar 12 09:14 /home/dev/projects/client-api/.env
</code></pre>
<p>Those trailing <strong>r--</strong> bits mean group members and every other user on the box can read a private key and a set of credentials. Tighten them so only the owner can access:</p>
<pre><code class="language-markdown">chmod 700 ~/.ssh              # directory: owner only
</code></pre>
<pre><code class="language-markdown">chmod 600 ~/.ssh/id_ed25519   # private key: owner read/write
</code></pre>
<pre><code class="language-markdown">chmod 644 ~/.ssh/id_ed25519.pub
</code></pre>
<pre><code class="language-markdown">chmod 600 ~/projects/client-api/.env
</code></pre>
<p>To sweep a whole projects directory at once:</p>
<pre><code class="language-markdown">find ~/projects -name ".env" -exec chmod 600 {} \;
</code></pre>
<p>Then give the actual hardening sequence:</p>
<ol>
<li><p>Enable full-disk encryption.</p>
</li>
<li><p>Keep the OS and security updates current.</p>
</li>
<li><p>Remove unnecessary administrator privileges.</p>
</li>
<li><p>Review SSH keys and remove unused ones.</p>
</li>
<li><p>Enable screen locking.</p>
</li>
<li><p>Configure endpoint monitoring where appropriate.</p>
<p>For example, if a developer's laptop has an old SSH key belonging to a previous project, remove it and revoke the corresponding access rather than leaving it available indefinitely.</p>
</li>
</ol>
<p>Some teams sidestep this risk at the source by moving development onto a <a href="https://v2cloud.com/glossary/virtual-desktop-infrastructure-vdi-definition">virtual desktop infrastructure</a>, where sensitive data lives centrally rather than on the physical machine. But this just relocates the DLP burden rather than removing it, since the virtual environment itself now needs the same access controls and monitoring to <a href="http://www.freecodecamp.org/news/vm-data-protection-best-practices/">protect VMs from data loss.</a></p>
<h3 id="heading-step-4-lock-down-containers-and-local-environments">Step 4: Lock Down Containers and Local Environments</h3>
<p>Local development increasingly happens inside containers, and each one is a small self-contained environment that can end up holding secrets if developers aren't careful about what gets stored in it.</p>
<p>A <code>.env</code> file might get baked into an image or credentials might sit in a container's environment variables long after a project wraps up.</p>
<p>Learning to work properly with <a href="https://www.freecodecamp.org/news/the-docker-handbook/">Docker</a> includes understanding how to keep secrets out of images entirely, using secret managers or runtime injection instead of hardcoding anything into a Dockerfile.</p>
<p>Here's a Dockerfile that looks perfectly straightforward and leaks in two different ways:</p>
<pre><code class="language-markdown">FROM node:20
WORKDIR /app
# Problem 1: copies everything, including .env, *.pem, and .git history
COPY . .
# Problem 2: the value is written into an image layer, permanently
ENV DB_PASSWORD="prod-9f2a-4c11-secret"
RUN npm install
CMD ["node", "server.js"]
</code></pre>
<p>Deleting the file in a later layer doesn't help, because the earlier layer still contains it. Anyone who pulls the image can read both:</p>
<pre><code class="language-markdown">docker history --no-trunc my-app:latest | grep -i password

docker run --rm -it --entrypoint sh my-app:latest -c "cat .env"
</code></pre>
<p>The corrected version starts with a .dockerignore, which keeps the sensitive files out of the build context entirely:</p>
<pre><code class="language-markdown"># .dockerignore

.env

.env.*

*.pem

*.key

.git

node_modules
</code></pre>
<p>Then copy only what the application needs and leave credentials out of the image:</p>
<pre><code class="language-markdown">FROM node:20

WORKDIR /app
COPY package*.json ./
RUN npm ci --omit=dev
# Copy application code only, not the whole directory
COPY src ./src
CMD ["node", "src/server.js"]
</code></pre>
<p>Supply the credential at runtime instead:</p>
<pre><code class="language-markdown">docker run -e DB_PASSWORD="$DB_PASSWORD" my-app:latest
</code></pre>
<p><strong>Practical check:</strong> Before pushing an image, scan it for .env files, private keys, credentials, and other sensitive data.</p>
<h3 id="heading-step-5-catch-leaks-before-they-ship">Step 5: Catch Leaks Before They Ship</h3>
<p>If you catch a leak early, it might cost you less loss.</p>
<p>Wire automated secret scanning into a <a href="https://www.freecodecamp.org/news/learn-continuous-integration-delivery-and-deployment/">CI/CD pipeline</a>, and a hardcoded API key gets flagged before it ever reaches a public repository.</p>
<p>Gitleaks and TruffleHog both handle this well by running right in the pipeline and failing the build as soon as a potential credential is detected in a commit.</p>
<p>Tuning the alerting properly takes some patience, but skipping that step tends to backfire. A scanner that keeps crying wolf with false positives encourages a team to click past every warning without even reading it. So, it's worth spending the setup time getting the rules to fit your codebase properly.</p>
<p>For example, a GitHub Actions workflow can run Gitleaks before code reaches production:</p>
<pre><code class="language-markdown">name: Secret Scan

on:

  pull_request:

  push:

jobs:

  gitleaks:

    runs-on: ubuntu-latest

    steps:

      - uses: actions/checkout@v4

        with:
          fetch-depth: 0

      - uses: gitleaks/gitleaks-action@v2
</code></pre>
<p>With this setup, the repository gets scanned whenever developers push code or open a pull request. If Gitleaks detects a potential credential, the workflow can stop before the change moves further through the deployment process.</p>
<p>A caught secret shows up in the workflow log looking roughly like this:</p>
<pre><code class="language-markdown">Finding:     AWS_SECRET_ACCESS_KEY=wJalrXUtnFEMI...
Secret:      wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY
RuleID:      aws-access-token
Entropy:     4.31
File:        services/billing/.env.staging
Line:        12
Commit:      8f2a1c9dbe4477a1c0f9e2b3a5d7c8e1f0a2b3c4
Author:      dev@example.com
INF 14 commits scanned.
WRN leaks found: 1
Error: Process completed with exit code 1.
</code></pre>
<p>The non-zero exit code is what actually blocks the merge. The file name, line number, and commit hash tell whoever picks it up exactly where to look.</p>
<p>Tuning the alerting properly takes some patience, but skipping that step tends to backfire. Again, you don't want constant false positives causing your team to click past every warning without looking at it. So take the time to get the rules to fit your codebase.</p>
<p>That tuning happens in a <strong>.gitleaks.toml</strong> at the repository root, where you exempt the paths that legitimately contain fake credentials:</p>
<pre><code class="language-markdown">[extend]
useDefault = true

[[rules]]
id = "aws-access-token"
  [rules.allowlist]
  paths = [
    '''tests/fixtures/.*''',
    '''docs/examples/.*'''
  ]
  regexes = [
    '''AKIAIOSFODNN7EXAMPLE'''
  ]
</code></pre>
<p>Keep the allowlist narrow. Exempting a whole directory because one file in it kept tripping the scanner is how real credentials start slipping through.</p>
<p>For example, a developer accidentally commits an API key to a pull request. Gitleaks flags it during the workflow, the pull request can't proceed, and the team removes the key and rotates the credential before merging the code.</p>
<h3 id="heading-step-6-extend-coverage-to-public-facing-surfaces">Step 6: Extend Coverage to Public-Facing Surfaces</h3>
<p>Most endpoint DLP strategies focus on laptops and dev machines, but public websites need the same scrutiny, even when they're maintained outside the engineering team.</p>
<p>You might have a poorly configured contact form, a staging subdomain someone forgot was still live, and an admin panel nobody locked down properly. An ⁠API security testing platform can test exposed APIs for vulnerabilities and business logic flaws before they put sensitive data at risk. Any one of those can leak data just as easily as a careless commit ever could.</p>
<p>Part of the problem is that web design and security often get treated as separate jobs handled by separate people. Sites built with hosting, maintenance, and security combined into the process from the start hold up far better than ones where those pieces get tacked on after something goes wrong.</p>
<p>This kind of ongoing oversight matters for a public-facing site the same way endpoint monitoring matters for a developer's machine. Unattended surfaces are where problems tend to build up slowly, unnoticed, until something forces a closer look.</p>
<p>If your dev team owns the marketing site too, treat it as a part of the same DLP scope, handled with the same routine attention as everything else, rather than something someone gets to whenever there's some spare time.</p>
<p>A simple monthly check can catch these issues before they become forgotten infrastructure. Start by listing every active domain and subdomain, then check whether each one still needs to be publicly accessible.</p>
<p>Here's how that usually plays out in practice. A team ships a customer portal rewrite and spins up <strong>staging-v2.example.com</strong> to demo it to stakeholders. The launch goes fine. Nobody deletes the staging environment, and it keeps running on a copy of the production database that was loaded for the demo.</p>
<p>Eight months later, a routine subdomain sweep turns it up:</p>
<pre><code class="language-markdown"># Every subdomain still resolving
dig +short staging-v2.example.com
203.0.113.47
</code></pre>
<pre><code class="language-markdown"># Is it publicly reachable, and does it need auth?
curl -s -o /dev/null -w "%{http_code}\n" https://staging-v2.example.com/api/v1/customers
200
</code></pre>
<p>A 200 with no credentials attached is the problem. Pulling the first record confirms it:</p>
<pre><code class="language-markdown">curl -s https://staging-v2.example.com/api/v1/customers | head -c 200
[{"id":4471,"email":"real.customer@example.com","phone":"+1-555-0142",
"plan":"enterprise","last_invoice":"2025-11-03"}]
</code></pre>
<p>That's production customer data sitting on an unauthenticated endpoint, indexed by anyone scanning certificate transparency logs for subdomains. The fix has an order to it:</p>
<ol>
<li><p>Take the environment offline immediately, before anything else.</p>
</li>
<li><p>Check access logs to see if anyone else found it first.</p>
</li>
<li><p>Decide whether the environment is still needed. If not, delete it and remove the DNS record.</p>
</li>
<li><p>If it's needed, put it behind authentication or an IP allowlist and replace the database with generated test data.</p>
</li>
<li><p>Add every subdomain to the monthly inventory so the next one doesn't sit unnoticed for eight months.</p>
</li>
</ol>
<p>Keep the same review for public forms, admin panels, cloud storage, and unused API endpoints. The goal is to make every internet-facing surface something the team knows about and actively maintains.</p>
<h3 id="heading-step-7-monitor-measure-and-keep-policies-honest">Step 7: Monitor, Measure, and Keep Policies Honest</h3>
<p>A DLP strategy without measurement isn't a good strategy. Teams need visibility into where data exposure is showing up in practice, the same way marketing teams have started tracking brand visibility across AI platforms.</p>
<p>For insurance, Similarweb’s <a href="https://aisearch.similarweb.com/aeo/">AEO platform</a> built for that purpose tracks where and how a brand gets mentioned across AI-generated answers, catching patterns nobody would ever spot by checking one prompt at a time by hand.</p>
<p>Security monitoring for endpoints runs on that same idea. Without a dashboard showing where secrets are exposed or which machines have drifted out of compliance, a team spends its energy cleaning up after incidents instead of catching them early.</p>
<p>The same principle applies to backups: an untested backup is merely an assumption. Regularly <a href="https://www.freecodecamp.org/news/disaster-recovery-testing/">testing your disaster recovery plan</a> ensures that your data backup strategy works properly when issues arise.</p>
<p>That principle doesn't stop at the laptop, either. Most companies store sensitive data across Microsoft 365 (mailboxes, SharePoint sites, OneDrive folders, or Teams channels) and it's usually the dev or IT team's job to make sure that data is actually protected, not just retained.</p>
<p>Microsoft's native retention covers accidental deletion within a short window. It isn't built to recover from ransomware or a compromised account that goes unnoticed for weeks. Teams relying on that retention alone, without an <a href="https://www.nakivo.com/microsoft-office-365-backup">independent backup for Microsoft 365</a> data, are making the same unverified assumption this article already warns against with local backups.</p>
<p>Policy matters here just as much as any of the tooling does.</p>
<p>A policy only works if the people bound by it actually believe in it, and that part is harder to measure than tooling compliance. This is where an <a href="https://confiscore.com/">anonymous employee confidence tool</a> earns its place, showing whether a team genuinely trusts a security rule or is just quietly routing around it.</p>
<p>Security policies have a similar challenge. Vague one-size-fits-all rules dropped on a team without context can be difficult for developers to follow in practice. Give a team a policy nobody understands and eventually they'll build a workaround for it, regardless of how well-meaning it was when someone wrote it.</p>
<p>Rules that are specific and come with a clear explanation tend to hold up better than a generic PDF buried three folders deep in onboarding.</p>
<p>A security dashboard simplifies the review process. Track the number of secrets detected, endpoints outside required controls, permission changes, and resolution times for each issue.</p>
<p>For a team of twenty developers, that dashboard doesn't need to be more complicated than this:</p>
<table style="min-width:207px"><colgroup><col style="min-width:25px"><col style="min-width:25px"><col style="width:107px"><col style="min-width:25px"><col style="min-width:25px"></colgroup><tbody><tr><th><p>Metric</p></th><td><p>Last month&nbsp;</p></td><td><p>This month&nbsp;</p></td><th><p>Target</p></th><th><p>Direction</p></th></tr><tr><td><p>Secrets caught in CI</p></td><td><p>6</p></td><td><p>2</p></td><td><p>0</p></td><td><p>Improving</p></td></tr><tr><td><p>Secrets found in merged code</p></td><td><p>1</p></td><td><p>0</p></td><td><p>0</p></td><td><p>Improving</p></td></tr><tr><td><p>Endpoints with full-disk encryption</p></td><td><p>17 / 20</p></td><td><p>20 / 20&nbsp;</p></td><td><p>100%</p></td><td><p>Met</p></td></tr><tr><td><p>Machines missing security updates (30+ days)</p></td><td><p>4</p></td><td><p>5</p></td><td><p>0</p></td><td><p>Worsening</p></td></tr><tr><td><p>Standing production DB access</p></td><td><p>9 users&nbsp;</p></td><td><p>3 users&nbsp;</p></td><td><p>0</p></td><td><p>Met</p></td></tr><tr><td><p>Unreviewed subdomains</p></td><td><p>11</p></td><td><p>0</p></td><td><p>0</p></td><td><p>Met</p></td></tr><tr><td><p>Median time to rotate an exposed credential</p></td><td><p>3 days&nbsp;</p></td><td><p>6 hours</p></td><td><p>Under 24 hours&nbsp;</p></td><td><p>Improving&nbsp;</p></td></tr></tbody></table>

<p>Two things stand out immediately in a table like that, and neither would be obvious from an incident report.</p>
<ol>
<li><p>Secrets are getting caught in CI instead of after merge, which means step 5 is doing its job.</p>
</li>
<li><p>Patching is going backwards, which means something in the update process isn't working and another reminder email is unlikely to fix it.</p>
</li>
</ol>
<p>For example, if you see that a secret scanner detected an AWS credential in a pull request, stop the merge, revoke or rotate the credential, remove it from the repository, and check for its presence elsewhere. Investigate why the credential was accessible to the developer and update the workflow to prevent recurrence.</p>
<p>Make sure to monitor these metrics regularly instead of waiting for an incident. If the same violations continue, consider implementing clearer policies, improved tools, or a streamlined development process rather than issuing additional warnings.</p>
<img src="https://cdn.hashnode.com/uploads/covers/65e715387099ff28d36bf4ac/6164df73-1f9b-440d-af32-606538d1c389.png" alt="Data loss prevention diagram showing five steps (discover and classify, monitor and inspect, enforce policy, report and log, and refine and tune)" style="display: block;" width="1006" height="591" loading="lazy">

<h2 id="heading-build-security-into-how-your-team-already-works">Build Security Into How Your Team Already Works</h2>
<p>A strong DLP strategy doesn't mean slowing engineers down with endless approval steps or locking every laptop into a rigid corporate image, and it shouldn't come at the cost of customer experience either, since the whole point of shipping fast is to serve users well without exposing their data in the process.</p>
<p>The strongest endpoint DLP strategies just run in the background, mostly invisible day to day: access control keeping blast radius small, CI/CD checks catching mistakes before they ship, and monitoring pointing a team toward its real gaps instead of leaving everyone to guess.</p>
<p>Start with access control and CI/CD, since those two catch common mistakes with minimal <a href="https://codedesign.ai/glossary/frictionless-ux">friction</a>, and build outward once that foundation is solid.</p>
<p>Picking up suitable tools, including the best <a href="https://infomsp.com/top-10-backup-software/">backup software</a>, behind each of these steps makes the whole thing far easier to build and keep running over time.</p>
<p>Anyone looking to strengthen those foundations, whether that's Linux fundamentals, containerization, or CI/CD pipelines, will find free and practical guides covering precisely this kind of work over at <a href="https://www.freecodecamp.org">freeCodeCamp</a>.</p>
 ]]>
                </content:encoded>
            </item>
        
            <item>
                <title>
                    <![CDATA[ How AI Is Changing Patching and What Devs Need to Know About Exposure Management ]]>
                </title>
                <description>
                    <![CDATA[ When a vulnerability scanner reports 23 vulnerabilities in your application, of which 4 are critical, 7 are high, and the remaining 12 are medium, at first glance the answer seems clear: start patchin ]]>
                </description>
                <link>https://www.freecodecamp.org/news/how-ai-is-breaking-traditional-patch-management/</link>
                <guid isPermaLink="false">6a9aefa26eac286787fb7078</guid>
                
                    <category>
                        <![CDATA[ Artificial Intelligence ]]>
                    </category>
                
                    <category>
                        <![CDATA[ cybersecurity ]]>
                    </category>
                
                    <category>
                        <![CDATA[ Vulnerability management ]]>
                    </category>
                
                    <category>
                        <![CDATA[ Patch management ]]>
                    </category>
                
                    <category>
                        <![CDATA[ DevSecOps ]]>
                    </category>
                
                    <category>
                        <![CDATA[ software security ]]>
                    </category>
                
                    <category>
                        <![CDATA[ Exposure Management ]]>
                    </category>
                
                <dc:creator>
                    <![CDATA[ Reetain Raina ]]>
                </dc:creator>
                <pubDate>Fri, 04 Sep 2026 16:19:46 +0000</pubDate>
                <media:content url="https://cdn.hashnode.com/uploads/covers/5e1e335a7a1d3fcc59028c64/ce5b87e6-1941-493c-a2c9-7822138160d6.png" medium="image" />
                <content:encoded>
                    <![CDATA[ <p>When a vulnerability scanner reports 23 vulnerabilities in your application, of which 4 are critical, 7 are high, and the remaining 12 are medium, at first glance the answer seems clear: start patching. But which one should you fix first?</p>
<p>This has always been an issue in vulnerability management. While a security team might find out about the vulnerable dependency, fixing it may not always be possible at once. Developers need to ensure that the vulnerable code is in use and perform all necessary checks before releasing the fix into production.</p>
<p>Recently, though, there have been some solid advancements in the use of AI for discovering software vulnerabilities and exploits. This <a href="https://dl.acm.org/doi/10.1145/3708522">research</a>, for example, details some of the findings and the path forward.</p>
<p>But how will this really help the development community? We need to fix things more quickly, but more importantly, we need to be able to figure out which vulnerabilities actually matter and which ones need attention first.</p>
<p>In this article, we'll examine what the classic patching process looks like, how AI is decreasing the amount of time security teams have to react, and why it's not always reasonable just to address vulnerabilities by their severity score.</p>
<p>We'll also discuss exposure management and the difference between it and traditional vulnerability management. Then we'll cover how developers can analyze dependencies, code reachability, and Software Bill of Materials (SBOMs) to figure out the actual vulnerabilities in their applications.</p>
<h3 id="heading-what-well-cover">What We'll Cover:</h3>
<ul>
<li><p><a href="#heading-patching-vs-exposure-management-whats-the-difference">Patching vs. Exposure Management: What's the Difference?</a></p>
<ul>
<li><p><a href="#heading-what-is-patching">What Is Patching?</a></p>
</li>
<li><p><a href="#heading-what-is-exposure-management">What Is Exposure Management?</a></p>
</li>
</ul>
</li>
<li><p><a href="#heading-the-old-patch-management-workflow-was-built-around-time">The Old Patch Management Workflow Was Built Around Time</a></p>
</li>
<li><p><a href="#heading-ai-is-shrinking-the-time-between-found-and-exploited">AI Is Shrinking the Time Between "Found" and "Exploited"</a></p>
</li>
<li><p><a href="#heading-why-patch-everything-doesnt-work-at-scale">Why "Patch Everything" Doesn't Work at Scale</a></p>
</li>
<li><p><a href="#heading-exposure-management-moving-from-flaw-counts-to-contextual-risk">Exposure Management: Moving from Flaw Counts to Contextual Risk</a></p>
</li>
<li><p><a href="#heading-the-dependency-tree-as-an-attack-surface">The Dependency Tree as an Attack Surface</a></p>
</li>
<li><p><a href="#heading-practical-takeaways-for-developers">Practical Takeaways for Developers</a></p>
<ul>
<li><p><a href="#heading-audit-transitive-dependencies">Audit Transitive Dependencies</a></p>
</li>
<li><p><a href="#heading-check-code-reachability">Check Code Reachability</a></p>
</li>
<li><p><a href="#heading-generate-an-sbom-in-cicd">Generate an SBOM in CI/CD</a></p>
</li>
<li><p><a href="#heading-use-compensating-controls-when-a-patch-isnt-ready">Use Compensating Controls When a Patch Isn't Ready</a></p>
</li>
<li><p><a href="#heading-apply-least-privilege-at-runtime">Apply Least Privilege at Runtime</a></p>
</li>
</ul>
</li>
<li><p><a href="#heading-wrap-up">Wrap Up</a></p>
</li>
</ul>
<h2 id="heading-patching-vs-exposure-management-whats-the-difference">Patching vs. Exposure Management: What's the Difference?</h2>
<p>Before we look at how AI is changing vulnerability response, it helps to understand two important concepts: patching and exposure management.</p>
<h3 id="heading-what-is-patching">What Is Patching?</h3>
<p>Patching is a process of updating software to address an existing issue, including a security vulnerability, bug, or a stability problem. This may involve upgrading a library with a known vulnerability, applying a security update for your operating system, or using a new release of an application with a vulnerability fixed.</p>
<p>For instance, if your application uses a particular library with a known security vulnerability, you can upgrade the library once the patched version is available. After that, you'll need to test the update, ensure that the application still operates as intended, and release the upgraded version into production.</p>
<p>So patching isn't only about installing the latest version of the package in question. A dependency update can break an API, some functionality, or even other dependent packages. That's why teams typically use patch management strategies when identifying vulnerabilities, updating decision-making, testing, deploying, and verifying that the patches work.</p>
<h3 id="heading-what-is-exposure-management">What Is Exposure Management?</h3>
<p>While vulnerability management is concerned mainly with identifying vulnerabilities, exposure management focuses more broadly on whether those vulnerabilities can actually provide a realistic route for an attack.</p>
<p>For example, a vulnerable library, limited in use to a development system, poses less immediate threat than a similarly vulnerable library used in an internet-facing system that's capable of accessing the database.</p>
<p>Some aspects to consider include accessibility via network, asset exposure, vulnerable code paths, identity and access controls, cloud environments, and the sensitivity of systems and data.</p>
<p>Simply put, vulnerability management is concerned with the discovery and tracking of vulnerabilities, whereas exposure management is focused on determining which of those vulnerabilities pose a true or larger risk.</p>
<h2 id="heading-the-old-patch-management-workflow-was-built-around-time">The Old Patch Management Workflow Was Built Around Time</h2>
<p>The classic process of vulnerability mitigation depended on step-by-step actions.</p>
<ol>
<li><p>CVE discovered</p>
</li>
<li><p>Security team assesses the severity</p>
</li>
<li><p>Maintainer releases an upstream patch</p>
</li>
<li><p>Developer updates the dependency</p>
</li>
<li><p>CI/CD pipeline runs regression tests</p>
</li>
<li><p>Production deployment</p>
</li>
<li><p>Remediation verified</p>
</li>
</ol>
<p>The process wasn't flawed by nature, but it operated under the unspoken premise that the defenders were granted enough room to operate through each step.</p>
<p>Let's take a dependency vulnerability example for practice. If the automated scanner detects a vulnerability within a popular utility package such as <strong>lodash</strong>, engineers don't immediately bump the dependency version in the production environment. They need to confirm if the application code leverages the vulnerable function, check the presence of breaking API changes after the upgrade, and run build validation through integration tests.</p>
<p>Each security patch is essentially a change to the code and needs to be safely pushed through the development and deployment cycle.</p>
<h2 id="heading-ai-is-shrinking-the-time-between-found-and-exploited">AI Is Shrinking the Time Between "Found" and "Exploited"</h2>
<p>The buffer between vulnerability discovery and exploitation that used to exist is being eliminated. This is because automated programs can scan through codebases, generate proofs of concept, and discover edge cases.</p>
<p>Modern AI systems help researchers and would-be attackers alike perform tasks like static binary analysis, detecting vulnerabilities, creating exploit payloads, and finding logical issues in complicated software designs.</p>
<p>Programs such as <a href="https://www.darpa.mil/news/2024/ai-cyber-challenge-cybersecurity">DARPA’s Artificial Intelligence Cyber Challenge</a> (AIxCC) show how AI systems can be used to automatically find and patch vulnerabilities in complex open-source software. During the 2024 semifinal competition, autonomous Cyber Reasoning Systems were tested against projects based on real-world software such as Jenkins, the Linux kernel, Nginx, SQLite3, and Apache Tika. The systems discovered 22 unique synthetic vulnerabilities and successfully patched 15 of them. They also identified one real-world bug in SQLite3, which was responsibly disclosed.</p>
<p>In the context of a real development process, certain tasks in the patching process can be performed by AI. The AI could perform code analysis and dependency analysis in order to detect potential vulnerabilities. It could also help trace the usage of vulnerable functions, recommend changes in code and dependencies, and generate tests to make sure that the suggested patch doesn’t break the existing functionality. The security team could also use AI for pattern detection.</p>
<p>As AI tools get better at assessing software and detecting vulnerabilities, the window of time between vulnerability detection and its mitigation becomes smaller and more important. This change in paradigm also affects how security professionals approach <a href="https://www.axonius.com/blog/from-vulnpocalypse-to-patchmageddon-security-ops-in-the-ai-era">AI and exposure management</a>, especially as the exploit window gets smaller and vulnerabilities need proper prioritization.</p>
<p>AI can also support exposure management by connecting vulnerability information with the environment in which the vulnerable software is running. For example, an AI-assisted security system could correlate a vulnerable dependency with an internet-facing application, its network connections, cloud permissions, and the data or services it can access. This helps security teams move from simply asking whether a vulnerability exists to asking <strong>what an attacker could realistically reach through it</strong>.</p>
<h2 id="heading-why-patch-everything-doesnt-work-at-scale">Why "Patch Everything" Doesn't Work at Scale</h2>
<p>When an organization-wide scanner generates a list of 500 vulnerabilities spread across multiple microservices, reacting to each one with urgency starts to seem impossible. Developers can suffer from alert fatigue and get overwhelmed pretty easily.</p>
<p>The <a href="https://nvd.nist.gov/vuln-metrics/cvss">Common Vulnerability Scoring System</a> (CVSS) is a standardized framework used to describe the severity of a vulnerability. CVSS v3.1 uses the following severity ranges:</p>
<table style="min-width:50px"><colgroup><col style="min-width:25px"><col style="min-width:25px"></colgroup><tbody><tr><td><p><strong>CVSS Score&nbsp;</strong></p></td><td><p><strong>Severity&nbsp;</strong></p></td></tr><tr><td><p>0.0&nbsp;</p></td><td><p>None&nbsp;</p></td></tr><tr><td><p>0.1–3.9&nbsp;</p></td><td><p>Low&nbsp;</p></td></tr><tr><td><p>4.0–6.9&nbsp;</p></td><td><p>Medium&nbsp;</p></td></tr><tr><td><p>7.0–8.9&nbsp;</p></td><td><p>High&nbsp;</p></td></tr><tr><td><p>9.0–10.0&nbsp;</p></td><td><p>Critical&nbsp;</p></td></tr></tbody></table>

<p>CVSS is useful because it provides both developers and security teams with a common language that describes the severity of a vulnerability. Nevertheless, the rating describes the vulnerability but not the environment where this vulnerability appears. In other words, CVSS doesn't tell you if the functionality used by the vulnerability is really used by your application or if the affected system is exposed to the Internet.</p>
<p>To see why CVSS alone isn't always enough, let's say we have two hypothetical vulnerabilities in an organization's environment:</p>
<ul>
<li><p><strong>Vulnerability A:</strong> This critical remote code execution vulnerability is part of an isolated testing harness or development-only dependency that's never included in the production environment and doesn't have any external network accessibility.</p>
</li>
<li><p><strong>Vulnerability B:</strong> A high-severity input validation vulnerability is found in an internet-facing API gateway that processes malicious user input and has access to a backend database with customer information.</p>
</li>
</ul>
<p>Looking at just the CVSS score would require the team to focus on Vulnerability A before Vulnerability B. But it's clear that Vulnerability B poses the greater threat to operations. Security studies show us that very few vulnerabilities get exploited once they're known. Telemetry data from the <a href="https://www.runzero.com/resources/kevology/">CISA KEV Catalog</a> clearly indicates that attackers focus on a subset of vulnerabilities that have a real path of exploitation.</p>
<p>In reality, teams must consider the CVSS score in addition to many contextual factors when deciding what to fix. A particular vulnerability might have a higher priority if the following conditions are true:</p>
<ul>
<li><p>it has an impact on an internet-facing production system,</p>
</li>
<li><p>there's a known exploit for the vulnerability,</p>
</li>
<li><p>there's sensitive information exposed,</p>
</li>
<li><p>it impacts an important business function,</p>
</li>
<li><p>or it offers an attacker a means of gaining access to other privileged systems.</p>
</li>
</ul>
<p>But vulnerabilities that occur only in development or are inaccessible for some reason likely don't need to be fixed immediately.</p>
<p>The most appropriate method for determining the importance of vulnerabilities is asking some straightforward questions: Is the vulnerable system accessible? Is the vulnerable code accessible? Is there any exploit for this vulnerability? What privileges does the affected service have? What will an attacker be able to access after exploiting the vulnerability?</p>
<p>Assigning the same level of priority to all alerts wastes engineering efforts on vulnerabilities that might pose no or little risk at all.</p>
<h2 id="heading-exposure-management-moving-from-flaw-counts-to-contextual-risk">Exposure Management: Moving from Flaw Counts to Contextual Risk</h2>
<p>Exposure management shifts focus from simply cataloging static vulnerabilities to evaluating an organization's actual operational risk posture.</p>
<p>Instead of asking "How many CVEs exist in our repositories?", exposure management asks "Which vulnerable components, misconfigurations, and reachable network paths create exploitable risk across our running assets?"</p>
<p>The difference becomes easier to see when you look at what each approach focuses on:</p>
<table style="min-width:75px"><colgroup><col style="min-width:25px"><col style="min-width:25px"><col style="min-width:25px"></colgroup><tbody><tr><td><p><strong>Dimension&nbsp;</strong></p></td><td><p><strong>Traditional Vulnerability Management&nbsp;</strong></p></td><td><p><strong>Exposure Management&nbsp;</strong></p></td></tr><tr><td><p>Primary Question&nbsp;</p></td><td><p>What software bugs and CVEs exist?&nbsp;</p></td><td><p>What paths can an attacker exploit to access critical assets?&nbsp;</p></td></tr><tr><td><p>Data Scope&nbsp;</p></td><td><p>Isolated dependency scans and static vulnerability databases&nbsp;</p></td><td><p>Code repositories, cloud runtime, network routing and IAM permissions&nbsp;</p></td></tr><tr><td><p>Prioritization Metric&nbsp;</p></td><td><p>CVSS base scores and static severity ratings&nbsp;</p></td><td><p>Reachability, exploitability, asset sensitivity and environment context&nbsp;</p></td></tr><tr><td><p>Primary Action&nbsp;</p></td><td><p>Upstream package upgrades and direct software patches&nbsp;</p></td><td><p>Risk-based triage: network isolation, configuration changes, or targeted patching&nbsp;</p></td></tr></tbody></table>

<p>When there are 10,000 cloud assets managed by an engineering environment and 1,000 vulnerable libraries found through dependency scanners, the combined numbers don't represent the actual security situation. In order to focus on the right level of risks, you should have some knowledge about the context within which each of these vulnerabilities exists.</p>
<ul>
<li><p>Is the container exposed to the internet or is it hidden behind the internal load balancer?</p>
</li>
<li><p>Is the code actually invoking the risky symbol or library function?</p>
</li>
<li><p>What are the identity permissions, cloud roles, and databases that are accessible through the vulnerable service?</p>
</li>
</ul>
<p>Having an understanding of this denominator (total numbers of assets that should receive a specific patch) helps teams identify the exposures that are actually threats so that engineering time is spent on solving the problems that impact production data.</p>
<h2 id="heading-the-dependency-tree-as-an-attack-surface">The Dependency Tree as an Attack Surface</h2>
<p>Modern software delivery depends on multi-layered packages such as npm, PyPI, Maven, NuGet, base operating system layer packages, GitHub Actions, and third-party APIs. The application logic is written by developers, but the final runtime software contains numerous levels of packages:</p>
<p>Your Application Logic → Direct Dependency (Declared in manifest) → Transitive Dependency (Pulled in automatically) → Underlying OS System Packages → Base Container Image / Cloud Runtime.</p>
<p>If a vulnerability is three levels down in the transitive dependencies and the transitive dependency is unmaintained, but can be accessed via external inputs, then that's an essential part of your application's attack surface.</p>
<p>That's why software development teams have started to use Software Bill of Materials (SBOMs). An SBOM is an inventory of software components that constitute the software or application. Depending on the technology used to create the SBOM, different information can be available including component name, version, dependencies and package ID.</p>
<p>It's helpful in cases where a new vulnerability has been identified. For example, if a vulnerability is discovered in a specific version of lodash, the security team can use its SBOMs to identify which applications or container images contain the affected version. They'll then be able to investigate if there's an exploitable exposure.</p>
<p>An SBOM alone doesn't provide security for the application. Its significance lies in increasing visibility to developers and security personnel regarding what is present in their applications.</p>
<h2 id="heading-practical-takeaways-for-developers">Practical Takeaways for Developers</h2>
<p>These principles will be relevant once you integrate them into your team's routine development process. There are some practical ways you and your team can employ these best practices and strategies:</p>
<h3 id="heading-audit-transitive-dependencies">Audit Transitive Dependencies</h3>
<p>The first thing to do is to verify what dependencies are included in your application. This is very useful when it comes to transitive dependencies, because these dependencies may have been automatically added when installing some other package directly.</p>
<p>For Node.js applications, the command <code>npm ls</code> will display the dependency tree. Python programmers may use <code>pipdeptree</code>, while Java programs created using Maven can use the <code>mvn dependency:tree</code> command. These commands can help you understand the origins of packages and the direct dependency that introduced a vulnerable transitive dependency into your project.</p>
<h3 id="heading-check-code-reachability">Check Code Reachability</h3>
<p>Finding a weak point in a dependency doesn't automatically imply that you're using it within your application. Don't take every vulnerability report as a critical production blocker. Instead, you should investigate if the impacted functionality is actually accessible from your application.</p>
<p>Suppose you find a vulnerability in a certain library function. In this case, you need to look through your codebase for any usage of this function and figure out if there's any possibility of passing user-controlled data to it. You may use either the search function provided by your IDE or command-line utilities such as grep.</p>
<p>An unused or inaccessible from the outside function reduces the urgency of the finding. But it doesn't automatically imply that you should ignore it.</p>
<h3 id="heading-generate-an-sbom-in-cicd">Generate an SBOM in CI/CD</h3>
<p>You can also produce a Software Bill of Materials using your <strong>CI/CD pipeline</strong>. Creating an SBOM will help you identify the components used in the software and make it easier to identify affected components once a vulnerability is found.</p>
<p>For instance, using Syft, you can generate an SBOM from a container image by executing the command: <code>syft my-app:latest -o cyclonedx-json &gt; sbom.json</code>.</p>
<p>This will create a CycloneDX JSON file with information on the components within the container image. This SBOM will then be stored along with the build artifacts. Once a new vulnerability is identified in a particular package version, it becomes easy for the security team to know which applications and container images contain this particular component.</p>
<h3 id="heading-use-compensating-controls-when-a-patch-isnt-ready">Use Compensating Controls When a Patch Isn't Ready</h3>
<p>Sometimes there may be no patch available or it may be too risky to implement it straight away since doing so might introduce breaking changes that need further testing. In such cases, you can use compensatory controls to lessen the exposure of the application until it's patched properly.</p>
<p>Depending on the environment, this may involve limiting the network access to the vulnerable component, isolating the workload from sensitive resources, disabling the feature that has been compromised, or minimizing the privileges of the application.</p>
<p>These controls don't take the place of the security patch but only minimize the risk of exploitation until a patch is implemented.</p>
<h3 id="heading-apply-least-privilege-at-runtime">Apply Least Privilege at Runtime</h3>
<p>Lastly, restrict the amount of access your applications have at run time. When a compromise is made due to exploitation, it prevents the spread of that breach to other applications or systems.</p>
<p>When deploying container-based applications, you can leverage read-only filesystems, as well as remove capabilities that aren't required in Linux. For instance, Docker provides the option to use <code>--read-only</code> and <code>--cap-drop=ALL</code> when running containers.</p>
<p>Cloud applications also need to adopt the same concept by ensuring the use of IAM permissions, giving access only to what the application requires.</p>
<p><strong>The goal is simple:</strong> if one element has been breached, the attacker should be able to gain access to as few components of the environment around it as possible.</p>
<p>The future of software security doesn't rely on how fast organizations can update their packages without knowing the underlying reasons for doing so. With vulnerability detection becoming more efficient through automation, effective mitigation relies on knowledge about the relationship between the source code, its dependencies, and infrastructure at runtime.</p>
<p>The purpose here isn't just finding new vulnerabilities but recognizing the ones that pose real risks to the application.</p>
<h2 id="heading-wrap-up">Wrap Up</h2>
<p>While artificial intelligence is helping teams detect vulnerabilities quicker, modern applications keep becoming increasingly dependent on numerous software layers. This doesn't mean that patching becomes unnecessary. It means that all vulnerabilities don't require the same immediate attention.</p>
<p>Developers also still need to look at where those vulnerabilities exist, whether they're reachable by attackers, and what they could affect. As the time between vulnerability detection and exploitation continues to change, understanding exposure becomes just as important as the patch itself.</p>
 ]]>
                </content:encoded>
            </item>
        
            <item>
                <title>
                    <![CDATA[ How to Protect Your Privacy Online in 2026 ]]>
                </title>
                <description>
                    <![CDATA[ Online privacy has never been more talked about, yet it has never been more misunderstood. In 2026, most people believe they are “covered” because they use a VPN, browse in incognito mode, or occasion ]]>
                </description>
                <link>https://www.freecodecamp.org/news/how-to-protect-your-privacy-online-in-2026/</link>
                <guid isPermaLink="false">6a0c88ab88372774116b600b</guid>
                
                    <category>
                        <![CDATA[ privacy ]]>
                    </category>
                
                    <category>
                        <![CDATA[ Security ]]>
                    </category>
                
                    <category>
                        <![CDATA[ cybersecurity ]]>
                    </category>
                
                <dc:creator>
                    <![CDATA[ Manish Shivanandhan ]]>
                </dc:creator>
                <pubDate>Tue, 19 May 2026 15:58:35 +0000</pubDate>
                <media:content url="https://cdn.hashnode.com/uploads/covers/5fc16e412cae9c5b190b6cdd/99ba3119-3b43-45d9-bcef-e3024b92b1a0.png" medium="image" />
                <content:encoded>
                    <![CDATA[ <p>Online privacy has never been more talked about, yet it has never been more misunderstood.</p>
<p>In 2026, most people believe they are “covered” because they use a VPN, browse in incognito mode, or occasionally decline cookies. These actions create a sense of control, but they only address a small part of the problem.</p>
<p>The reality is more complex. Privacy today is not about a single tool or setting. It is about how data flows across systems, how identity is inferred, and how behavior is tracked even when you think you are anonymous.</p>
<blockquote>
<p>“<em>Arguing that you don't care about the right to privacy because you have nothing to hide is no different than saying you don't care about free speech because you have nothing to say.</em>”<br>Source: <a href="https://www.theguardian.com/us-news/video/2015/may/22/edward-snowden-rights-to-privacy-video">The Guardian</a></p>
</blockquote>
<p>If you want real protection, you need to understand what actually works and what only creates the illusion of safety.</p>
<h2 id="heading-table-of-contents">Table of Contents</h2>
<ul>
<li><p><a href="#heading-privacy-is-no-longer-about-hiding-your-ip">Privacy Is No Longer About Hiding Your IP</a></p>
</li>
<li><p><a href="#heading-the-illusion-of-incognito-mode">The Illusion of Incognito Mode</a></p>
</li>
<li><p><a href="#heading-the-rise-of-first-party-tracking">The Rise of First-Party Tracking</a></p>
</li>
<li><p><a href="#heading-encryption-still-matters-but-it-is-not-enough">Encryption Still Matters, But It Is Not Enough</a></p>
</li>
<li><p><a href="#heading-devices-are-the-new-weak-point">Devices Are the New Weak Point</a></p>
</li>
<li><p><a href="#heading-behavioral-data-is-the-real-commodity">Behavioral Data Is the Real Commodity</a></p>
</li>
<li><p><a href="#heading-where-vpns-actually-fit">Where VPNs Actually Fit</a></p>
</li>
<li><p><a href="#heading-identity-is-the-core-problem">Identity Is the Core Problem</a></p>
</li>
<li><p><a href="#heading-regulation-helps-but-it-has-limits">Regulation Helps, But It Has Limits</a></p>
</li>
<li><p><a href="#heading-what-actually-protects-you">What Actually Protects You</a></p>
</li>
<li><p><a href="#heading-the-trade-offs-are-real">The Trade-Offs Are Real</a></p>
</li>
<li><p><a href="#heading-the-future-of-privacy">The Future of Privacy</a></p>
</li>
<li><p><a href="#heading-closing-perspective">Closing Perspective</a></p>
</li>
</ul>
<h2 id="heading-privacy-is-no-longer-about-hiding-your-ip"><strong>Privacy Is No Longer About Hiding Your IP</strong></h2>
<p>A decade ago, privacy conversations centered on IP addresses. If you could mask your IP, you were considered relatively anonymous. That model is outdated.</p>
<p>Modern tracking systems rely on <a href="https://developer.mozilla.org/en-US/docs/Glossary/Fingerprinting">fingerprinting</a>. Your browser, device type, screen resolution, installed fonts, GPU behaviour, and even how you move your mouse can uniquely identify you. This means that even if your IP changes, your identity can still be reconstructed with high confidence.</p>
<p>Companies no longer need a single identifier. They build probabilistic profiles. These profiles combine dozens of weak signals into one strong identity.</p>
<p>This is why simply using a VPN does not guarantee privacy. It hides where you are connecting from, but it does not hide who you are behaving like.</p>
<h2 id="heading-the-illusion-of-incognito-mode"><strong>The Illusion of Incognito Mode</strong></h2>
<p>Incognito mode is one of the most misunderstood features in modern browsers. It does not make you anonymous. It simply prevents your local browser from saving history, cookies, and form data.</p>
<p>Your internet service provider can still see your activity. Websites can still track you. Third-party scripts can still build profiles. Incognito mode protects you from other users on the same device, not from the internet itself.</p>
<p>In 2026, relying on incognito mode for privacy is like closing your eyes and assuming no one can see you. It changes your local environment, not the external systems observing you.</p>
<h2 id="heading-the-rise-of-first-party-tracking"><strong>The Rise of First-Party Tracking</strong></h2>
<p>One major shift in recent years is the move from third-party tracking to first-party tracking. Browsers and regulators have restricted third-party cookies, but this has not reduced tracking. It has changed who does it.</p>
<p>Large platforms now collect data directly. When you log into services, your activity is tied to your account. This is more accurate than cookie-based tracking and harder to block.</p>
<p>Even when you are not logged in, platforms use techniques like <a href="https://digiday.com/marketing/wtf-link-decoration/">link decoration</a> and server-side tracking. These methods bypass traditional browser protections. As a result, blocking cookies is no longer enough.</p>
<p>Privacy today requires reducing how much data you generate, not just controlling how it is stored.</p>
<h2 id="heading-encryption-still-matters-but-it-is-not-enough"><strong>Encryption Still Matters, But It Is Not Enough</strong></h2>
<p>Encryption remains one of the most important tools in digital privacy. It ensures that data in transit cannot be easily intercepted.</p>
<p>HTTPS is now standard, and end-to-end encryption is widely used in messaging apps.</p>
<p>However, encryption protects content, not metadata.</p>
<p><a href="https://www.ibm.com/think/topics/metadata">Metadata</a> includes who you communicate with, when, how often, and from where. This data can reveal patterns that are often more valuable than the content itself.</p>
<p>For example, knowing that two people communicate regularly at specific times can be enough to infer relationships or activities.</p>
<p>In 2026, sophisticated surveillance systems rely heavily on metadata analysis. This means encryption is necessary, but it is not sufficient.</p>
<h2 id="heading-devices-are-the-new-weak-point"><strong>Devices Are the New Weak Point</strong></h2>
<p>Most privacy discussions focus on networks, but devices have become the primary attack surface. Smartphones, laptops, and even smart home devices continuously collect data.</p>
<p>Operating systems gather <a href="https://www.ibm.com/think/topics/telemetry">telemetry</a>. Apps request permissions that go far beyond their core function. Background processes transmit usage patterns, location data, and behavioral signals.</p>
<p>Even trusted platforms collect large amounts of data. This is often justified as necessary for improving services, but it creates detailed user profiles.</p>
<p>Real privacy requires controlling what your devices share. This includes limiting permissions, reducing app usage, and choosing systems that minimize data collection by design.</p>
<h2 id="heading-behavioral-data-is-the-real-commodity"><strong>Behavioral Data Is the Real Commodity</strong></h2>
<p>In 2026, raw personal data is less valuable than behavioral data. Companies are less interested in who you are and more interested in what you do.</p>
<p>Behavioral data includes browsing habits, purchase patterns, scrolling speed, typing rhythm, and engagement signals. This data feeds machine learning models and AI automation platforms that predict future actions.</p>
<p>These models power everything from targeted advertising to risk scoring. They are also used in fraud detection, hiring systems, and financial services.</p>
<p>As AI increasingly shapes online interactions, understanding how your data is analyzed can be valuable. It is also important to recognize whether content is generated or influenced by AI. AI detection platforms like <a href="https://gptzero.me/">ai checker</a> help users identify AI-generated content while supporting greater transparency in digital environments.</p>
<p>The challenge is that behavioral data is difficult to hide. It is generated passively through normal usage. Protecting privacy means reducing the amount of behavior that can be observed and linked over time.</p>
<h2 id="heading-where-vpns-actually-fit"><strong>Where VPNs Actually Fit</strong></h2>
<p>VPNs still have a role, but it is narrower than most people think. They are useful for securing connections on untrusted networks, such as public Wi-Fi. They can also help bypass geographic restrictions.</p>
<p>However, they do not make you anonymous. They shift trust from your internet provider to the VPN provider. If the provider logs data, your activity is still traceable.</p>
<p>This is where the market has evolved. Users are now looking beyond traditional VPNs such as NordVPN and exploring options that offer stronger privacy guarantees, such as decentralized networks or tools with strict no-logging architectures.</p>
<p>In this context, the idea of a traditional VPN alternatives often comes up, not as a rejection of VPNs, but as a recognition that privacy requires a broader approach.</p>
<p>The key is understanding that a VPN is one layer, not a complete solution.</p>
<h2 id="heading-identity-is-the-core-problem"><strong>Identity Is the Core Problem</strong></h2>
<p>At the center of modern privacy is identity. Every system you interact with tries to answer one question: is this the same user as before?</p>
<p>If the answer is yes, your actions can be linked over time. This creates a persistent profile.</p>
<p>Breaking this link is difficult. Logging into accounts, using the same device, and maintaining consistent behavior all reinforce identity. Even small signals can reconnect fragmented data.</p>
<p>True privacy requires disrupting this continuity. This can involve using separate environments for different activities, avoiding unnecessary logins, and limiting cross-platform data sharing.</p>
<p>It is not about being invisible. It is about being harder to correlate.</p>
<h2 id="heading-regulation-helps-but-it-has-limits"><strong>Regulation Helps, But It Has Limits</strong></h2>
<p>Privacy regulations have expanded globally. Laws now require companies to disclose data practices, obtain consent, and provide user controls.</p>
<p>These changes have improved transparency, but they have not fundamentally changed data collection. Consent banners are often designed to nudge users toward acceptance. Privacy policies remain complex and difficult to interpret.</p>
<p>Enforcement is also uneven. Large companies adapt quickly, while smaller players may ignore rules altogether.</p>
<p>Regulation sets boundaries, but it does not eliminate incentives. As long as data drives revenue, companies will find ways to collect it within legal frameworks.</p>
<h2 id="heading-what-actually-protects-you">What Actually Protects You</h2>
<p>Real privacy in 2026 does not come from one app, browser setting, or security tool. Privacy works best as a layered system where several habits work together. Tools help, but behavior matters more. Strong privacy comes from sharing less data, separating identities, reducing tracking signals, and using the right tools carefully.</p>
<p>The first step is to minimize data sharing. Every account signup, app download, connected service, and permission request creates another source of information collection. Share only what is necessary. Use fewer apps and services when possible. Avoid unnecessary integrations between platforms. Review permissions such as location, contacts, microphone access, and background tracking. Less information leaving your control means less information available to collect, sell, or track.</p>
<p>The next step is separating digital identity. Avoid linking every activity to the same account or profile. Use different emails, accounts, or even devices for work, personal use, and anonymous activities. Keeping activities separate makes it harder for systems to build one complete profile about you.</p>
<p>You should also reduce behavioral signals. Modern tracking systems use cookies, tracking pixels, app behavior, and device fingerprinting to identify users. Review app permissions and limit tracking where possible. Fewer signals make profiling harder.</p>
<p>Privacy-focused tools add another layer. Use secure browsers, encrypted messaging apps, secure DNS, and VPNs when needed. Keep them updated and properly configured. Privacy is not about becoming invisible. It is about staying intentional and keeping control over your information.</p>
<h2 id="heading-the-trade-offs-are-real"><strong>The Trade-Offs Are Real</strong></h2>
<p>It is important to acknowledge that privacy comes with trade-offs. More privacy often means less convenience. Personalized services become less accurate. Seamless experiences may require more manual effort.</p>
<p>Most users are not willing to sacrifice convenience entirely. This is why complete privacy is rare. Instead, the goal should be proportional privacy.</p>
<p>Protect what matters most. Accept some level of exposure where the cost of protection is too high.</p>
<h2 id="heading-the-future-of-privacy"><strong>The Future of Privacy</strong></h2>
<p>Looking ahead, privacy will become more integrated into system design. Technologies like on-device processing, differential privacy, and zero-knowledge proofs are gaining traction.</p>
<p>These approaches aim to reduce data collection while still enabling useful services. Instead of sending raw data to servers, computations happen locally or in privacy-preserving ways.</p>
<p>However, adoption will take time. Economic incentives still favor data collection. Until that changes, users remain responsible for their own privacy posture.</p>
<h2 id="heading-closing-perspective"><strong>Closing Perspective</strong></h2>
<p>The biggest misconception about online privacy is that it can be solved with a single tool. In reality, it is a continuous process.</p>
<p>What protects you in 2026 is not just technology, but how you use it. It is the combination of reducing data exposure, understanding tracking mechanisms, and making deliberate choices about your digital behavior.</p>
<p>Privacy is no longer about disappearing. It is about controlling how visible you are, to whom, and under what conditions.</p>
 ]]>
                </content:encoded>
            </item>
        
            <item>
                <title>
                    <![CDATA[ Why Chrome OS Is the Operating System the AI Era Was Built For ]]>
                </title>
                <description>
                    <![CDATA[ Chrome OS runs on a read-only filesystem. You can't install executables on the host. There's no traditional desktop environment. Everything that interacts with the underlying system does so through a  ]]>
                </description>
                <link>https://www.freecodecamp.org/news/why-chrome-os-is-the-ai-os/</link>
                <guid isPermaLink="false">69e2765cfd22b8ad62611ba8</guid>
                
                    <category>
                        <![CDATA[ Chrome OS ]]>
                    </category>
                
                    <category>
                        <![CDATA[ Linux ]]>
                    </category>
                
                    <category>
                        <![CDATA[ Cloud Computing ]]>
                    </category>
                
                    <category>
                        <![CDATA[ AI ]]>
                    </category>
                
                    <category>
                        <![CDATA[ AWS ]]>
                    </category>
                
                    <category>
                        <![CDATA[ cybersecurity ]]>
                    </category>
                
                <dc:creator>
                    <![CDATA[ Christopher Galliart ]]>
                </dc:creator>
                <pubDate>Fri, 17 Apr 2026 18:05:16 +0000</pubDate>
                <media:content url="https://cdn.hashnode.com/uploads/covers/5e1e335a7a1d3fcc59028c64/c4116a06-9e42-4da5-a152-0fe1433e0857.png" medium="image" />
                <content:encoded>
                    <![CDATA[ <p>Chrome OS runs on a read-only filesystem. You can't install executables on the host. There's no traditional desktop environment. Everything that interacts with the underlying system does so through a sandboxed browser, a containerized Linux terminal, or a cloud connection.</p>
<p>For years, that list of constraints was the reason people dismissed it. But in 2026, it's the reason Chrome OS might be the most correctly designed operating system for what's coming.</p>
<p>The security architecture treats the endpoint as untrusted by default. The containerized Linux environment gives developers a full headless stack without compromising the host. And an upcoming OS-level rewrite, Aluminium, puts Google's on-device AI models directly into the kernel.</p>
<p>This article covers security architecture, the container-based developer environment, cloud-streamed creative tools via AWS NICE DCV, cloud gaming, and what Aluminium OS means for on-device AI.</p>
<h3 id="heading-heres-what-well-cover">Here's what we'll cover:</h3>
<ol>
<li><p><a href="#heading-security-first-architecture-in-the-era-of-ai-powered-threats">Security-First Architecture in an Era of AI-Powered Threats</a></p>
</li>
<li><p><a href="#heading-a-headless-linux-stack-thats-more-flexible-than-it-looks">A Headless Linux Stack That's More Flexible Than It Looks</a></p>
</li>
<li><p><a href="#heading-aws-nice-dcv-changes-the-creative-tools-conversation">AWS NICE DCV Changes the Creative Tools Conversation</a></p>
</li>
<li><p><a href="#heading-cloud-gaming-works">Cloud Gaming Works</a></p>
</li>
<li><p><a href="#heading-aluminum-os-on-device-models-on-googles-own-architecture">Aluminium OS: On-Device Models on Google's Own Architecture</a></p>
</li>
<li><p><a href="#heading-where-this-lands">Where This Lands</a></p>
</li>
</ol>
<h2 id="heading-security-first-architecture-in-an-era-of-ai-powered-threats">Security-First Architecture in an Era of AI-Powered Threats</h2>
<p>Threat actors are getting better tools. Models like Mythos are lowering the barrier for generating convincing phishing campaigns, crafting polymorphic malware, and automating social engineering at scale.</p>
<p>Traditional operating systems present exactly the attack surface these tools target: writable system files, user-installable executables, patches that sit uninstalled for weeks because someone clicked "remind me later."</p>
<p>Chrome OS sidesteps most of this by design. The root filesystem is read-only and cryptographically verified on every boot through a process called Verified Boot.</p>
<p>If anything has modified the OS files since the last verified state, whether that's malware, a compromised package, or a rogue AI agent that decided to start deleting system files, the device detects it at startup and either self-corrects or refuses to boot.</p>
<p>Persistence across reboots isn't difficult. It's architecturally impossible through software alone.</p>
<p>Updates happen silently. While you're working, the system downloads the next OS version to an inactive partition. On your next reboot, it pivots to the updated version. No prompts, no deferred patches, no exposure window.</p>
<p>Major updates ship every four to six weeks. Security patches land every two to three weeks. The gap between vulnerability discovery and remediation is measured in days.</p>
<p>Chrome OS consistently doesn't appear in the top 50 products by CVE count in the NIST vulnerability database. Windows and the Linux kernel sit near the top every year. When AI is actively being weaponized to find and exploit vulnerabilities faster than humans can patch them, a read-only, verified, automatically updated endpoint is a different category of security posture.</p>
<p>The tradeoff is trust. Chrome OS's security model means trusting Google as the root authority for your entire computing stack: updates, certificate trust, telemetry. Organizations with strict data sovereignty requirements should weigh that dependency carefully.</p>
<h2 id="heading-a-headless-linux-stack-thats-more-flexible-than-it-looks">A Headless Linux Stack That's More Flexible Than It Looks</h2>
<p>Chrome OS is a text-based operating system. There's no native GUI layer. Stop and sit with that for a second, because it's the thing that makes people dismiss Chrome OS and also the thing that makes it work.</p>
<p>The entire graphical interface you interact with IS the Chrome browser. The Ash shell, Chrome's window manager, is the desktop. You don't install applications onto it the way you install .exe files on Windows or drag .app bundles into a macOS Applications folder. If it isn't running in a browser tab, an Android VM, or a Linux container, it doesn't run. That restriction is what keeps the host locked down, and it's what makes everything else possible.</p>
<p>Under the hood, Chrome OS runs a minimal virtual machine called Termina through crosvm, Google's Rust-based VM monitor.</p>
<p>Inside Termina, LXD manages Linux containers. The default container, penguin, is a Debian environment with a special trick: it bridges GUI-based Linux applications directly into the Chrome OS desktop through a Wayland proxy called Sommelier. Install VS Code, GIMP, or LibreOffice in penguin and they show up in your Chrome OS app launcher, running in windows alongside your browser tabs. For a lot of developers, penguin alone covers the daily workflow.</p>
<p>But Termina gives you more than penguin. Through the LXD layer you can spin up independent containers that are fully isolated operating systems: Arch, Alpine, Ubuntu, whatever you need.</p>
<p>These aren't attached to the GUI bridge. They run headless, natively, with their own systemd, their own package managers, their own persistent state. Need a clean Ubuntu environment to test a deployment script without touching your main setup? <code>lxc launch</code> and you're there. Need to blow it away? <code>lxc delete</code> and it's gone. No orphaned files on the host, no cross-contamination between environments.</p>
<p>The key distinction from Docker is that LXD runs system containers (full OS emulation) rather than application containers. You get background services, persistent daemons, the works. You can also run Docker inside any of these LXD containers if you need application-level containerization on top of that.</p>
<p>Snapshot your entire environment with <code>lxc snapshot</code> before a risky dependency install and roll back instantly if something breaks. That kind of safety net is broader than version control alone: it captures your full OS configuration, not just code.</p>
<p>Pair this with browser-native tools like GitHub Codespaces, Google Colab, AWS CloudShell, or vscode.dev, and the terminal handles your local tooling while the browser handles everything else.</p>
<p>AI coding assistants like Claude and Gemini already operate natively in the browser. The distance between "cloud IDE" and "local IDE" keeps shrinking.</p>
<p>There are friction points: no custom kernel modules inside Crostini. Nested KVM requires Intel Gen 10+ processors. VPN routing into the Linux container from the Chrome OS host can be a headache, with WireGuard requiring userspace workarounds inside the container.</p>
<p>But none of these break the core architecture for cloud-native work. They're just worth knowing about before you commit.</p>
<h2 id="heading-aws-nice-dcv-changes-the-creative-tools-conversation">AWS NICE DCV Changes the Creative Tools Conversation</h2>
<p>One of the longest-standing arguments against Chrome OS has been the absence of professional creative software. There's no Premiere, no DaVinci Resolve, no Blender, no Ableton. For years, this was a dead-end conversation.</p>
<p>AWS NICE DCV (Desktop Cloud Visualization) reopens it. DCV is a high-performance remote display protocol that streams GPU-accelerated desktop sessions from EC2 instances to any device, including a Chromebook running the browser-based DCV client. It supports OpenGL, Vulkan, and DirectX rendering, with adaptive encoding that adjusts to network conditions. On AWS, the DCV license is free. You pay only for the EC2 compute time.</p>
<p>Netflix engineers use DCV to stream content creation applications to remote artists. Volkswagen runs 3D CAD simulations across their engineering division through it. A VFX studio called RVX used it to deliver visual effects for HBO's The Last of Us, streaming Nuke, Maya, Houdini, and Blender to artists distributed across Europe from servers in Iceland. Their team said it was the best remote experience they'd ever worked with.</p>
<p>So: a Chromebook connected to a g5.xlarge EC2 instance (one A10G GPU) can run Blender, DaVinci Resolve, or any other GPU-accelerated creative application with full hardware acceleration. The rendering happens in the data center. DCV streams the pixels. The creative professional gets a responsive, high-fidelity workspace on a $400 machine that couldn't locally render a single frame.</p>
<p>The constraints are connectivity and cost. You need sustained bandwidth (25+ Mbps for 1080p work, more for 4K multi-monitor setups) and leaving a GPU instance running around the clock adds up. But for studios and professionals who already budget for high-end workstations, the math often pencils out, especially when you factor in zero local hardware maintenance and the ability to scale GPU power on demand.</p>
<h2 id="heading-cloud-gaming-works">Cloud Gaming Works</h2>
<p>GeForce NOW survived where Stadia failed because it made a better business decision: bring your own games. Connect your existing Steam, Epic, or Ubisoft library and stream from NVIDIA's server-side hardware. The Ultimate tier now runs on RTX 5080-class infrastructure. 4K at 120fps with ray tracing, on a fanless Chromebook.</p>
<p>Chrome OS has a structural advantage as a cloud gaming client. GeForce NOW runs natively in the Chromium browser via WebRTC, and users consistently report less micro-stuttering and tighter input handling than the standalone Windows desktop app. Under good network conditions, measured total latency runs 13 to 14ms, with sub-3ms ping documented near datacenter proximity. That's below human perceptual threshold for most game types.</p>
<p>Anti-cheat systems like Easy Anti-Cheat and Riot Vanguard are a non-issue in this model. They run on the server where the game executes, not on your local endpoint. On-device gaming isn't viable on Chrome OS and likely never will be. The architecture isn't designed for it, and even projects attempting to bridge local GPUs hit bottlenecks in the container layers. Cloud gaming is the path, and it works.</p>
<p>The limiting factors are network-dependent. Latency spikes above 500ms on bad connections make fast-twitch games unplayable, and NVIDIA's 100-hour monthly cap on the Ultimate tier has drawn criticism. But cloud gaming on Chrome OS has crossed the line from novelty to daily-driver viable for most use cases.</p>
<h2 id="heading-aluminium-os-on-device-models-on-googles-own-architecture">Aluminium OS: On-Device Models on Google's Own Architecture</h2>
<p>The most consequential near-term development for Chrome OS is Project Aluminium, a ground-up rewrite that replaces the current Chrome OS foundation with a native Android kernel. Not another bolted-on compatibility layer: a new operating system built on Android 16, designed to run Android applications natively with direct hardware acceleration instead of routing them through the resource-heavy ARCVM virtual machine that currently eats CPU cycles on even basic app launches.</p>
<p>The AI story is the real story. Aluminium is being built with Gemini models integrated directly into the OS: the file system, the application launcher, the window manager.</p>
<p>Google serving their own proprietary models on their own devices, using an architecture optimized specifically to run them, is a level of vertical integration that no other OS vendor has in the pipeline. Apple has the silicon advantage for local inference. Google has the model-to-OS integration advantage. Those are competing theses about where AI compute should live, and both are worth taking seriously.</p>
<p>The rollout timeline from court documents and leaked roadmaps puts a trusted tester program on select hardware in late 2026, premium tablets by early 2027, and general consumer availability in 2028. Chrome OS Classic gets maintained through existing support obligations until 2033 or 2034.</p>
<p>The launch won't be perfect. Google's track record on platform transitions gives the community earned skepticism. But the ability to iterate a natively AI-integrated OS on hardware they control is the kind of capability that compounds over time.</p>
<h2 id="heading-where-this-lands">Where This Lands</h2>
<p>Two years ago, calling Chrome OS a serious platform for development or creative work would have been a stretch. Today you can run a full Debian environment with systemd daemons, snapshot your workspace, stream Blender from a GPU-backed data center, play AAA games at 4K on hardware you don't own, and do all of it from a verified, read-only endpoint that patches itself while you sleep.</p>
<p>The remaining gaps are real. But they're concentrated in workflows that are themselves moving to the cloud. Chrome OS was designed around assumptions about computing that used to be premature. They're not premature anymore.</p>
 ]]>
                </content:encoded>
            </item>
        
            <item>
                <title>
                    <![CDATA[ How to Sync AWS Secrets Manager Secrets into Kubernetes with the External Secrets Operator ]]>
                </title>
                <description>
                    <![CDATA[ If someone asked you how secrets flow from AWS Secrets Manager into a running pod, could you explain it confidently? Storing them is straightforward. But handling rotation, stale env vars, and the gap ]]>
                </description>
                <link>https://www.freecodecamp.org/news/how-to-sync-aws-secrets-manager-secrets-into-kubernetes-with-the-external-secrets-operator/</link>
                <guid isPermaLink="false">69c541f010e664c5dadc877e</guid>
                
                    <category>
                        <![CDATA[ Kubernetes ]]>
                    </category>
                
                    <category>
                        <![CDATA[ cybersecurity ]]>
                    </category>
                
                    <category>
                        <![CDATA[ Devops ]]>
                    </category>
                
                    <category>
                        <![CDATA[ Terraform ]]>
                    </category>
                
                    <category>
                        <![CDATA[ secrets management ]]>
                    </category>
                
                    <category>
                        <![CDATA[ SRE ]]>
                    </category>
                
                <dc:creator>
                    <![CDATA[ Osomudeya Zudonu ]]>
                </dc:creator>
                <pubDate>Thu, 26 Mar 2026 14:25:52 +0000</pubDate>
                <media:content url="https://cdn.hashnode.com/uploads/covers/5e1e335a7a1d3fcc59028c64/6cca126e-dd50-4400-ae9d-65449581345b.png" medium="image" />
                <content:encoded>
                    <![CDATA[ <p>If someone asked you how secrets flow from AWS Secrets Manager into a running pod, could you explain it confidently?</p>
<p>Storing them is straightforward. But handling rotation, stale env vars, and the gap between what your pod reads and what AWS actually holds is where many engineers go quiet.</p>
<p>In this guide, you'll build a complete secrets pipeline from AWS Secrets Manager into Kubernetes pods. You'll provision the infrastructure with Terraform, sync secrets using the External Secrets Operator, and run a sample application that reads the same credentials in two different ways: via environment variables and via a volume mount.</p>
<p>By the end, you'll be able to:</p>
<ul>
<li><p>Explain the full architecture from vault to pod</p>
</li>
<li><p>Run the lab locally in about 15 minutes</p>
</li>
<li><p>Prove why environment variables go stale after rotation, while mounted secret files stay fresh</p>
</li>
<li><p>Deploy the same pattern on Amazon Elastic Kubernetes Service with OpenID Connect-based CI/CD</p>
</li>
<li><p>Troubleshoot the most common failures</p>
</li>
</ul>
<p>Below is an architecture diagram showing secrets flowing from AWS Secrets Manager through the External Secrets Operator into a Kubernetes Secret, then splitting into environment variables set at pod start and a volume mount that updates within 60 seconds.</p>
<img src="https://cdn.hashnode.com/uploads/covers/698d563262d4ce66226a844a/ac8bfc9e-304e-41b8-b6a3-7ce1795b29a9.png" alt="Architecture diagram showing secrets flowing from AWS Secrets Manager through the External Secrets Operator into a Kubernetes Secret, then splitting into environment variables set at pod start and a volume mount that updates within 60 seconds." style="display: block;" width="1024" height="1536" loading="lazy">

<h2 id="heading-table-of-contents">Table of Contents</h2>
<ul>
<li><p><a href="#heading-prerequisites">Prerequisites</a></p>
</li>
<li><p><a href="#heading-how-to-understand-the-secret-flow">How to Understand the Secret Flow</a></p>
</li>
<li><p><a href="#heading-how-to-run-the-local-lab">How to Run the Local Lab</a></p>
</li>
<li><p><a href="#heading-how-to-inspect-the-externalsecret-and-the-application">How to Inspect the ExternalSecret and the Application</a></p>
</li>
<li><p><a href="#heading-how-to-test-secret-rotation">How to Test Secret Rotation</a></p>
</li>
<li><p><a href="#heading-how-to-choose-between-external-secrets-operator-and-the-csi-driver">How to Choose Between External Secrets Operator and the CSI Driver</a></p>
</li>
<li><p><a href="#heading-how-to-deploy-the-pattern-on-amazon-elastic-kubernetes-service">How to Deploy the Pattern on Amazon Elastic Kubernetes Service</a></p>
</li>
<li><p><a href="#heading-how-to-configure-github-actions-without-stored-aws-credentials">How to Configure GitHub Actions Without Stored AWS Credentials</a></p>
</li>
<li><p><a href="#heading-how-to-troubleshoot-the-most-common-failures">How to Troubleshoot the Most Common Failures</a></p>
</li>
<li><p><a href="#heading-conclusion">Conclusion</a></p>
</li>
</ul>
<h2 id="heading-prerequisites">Prerequisites</h2>
<p>Before you begin, make sure you have the following tools installed and configured.</p>
<p><strong>For the local lab:</strong></p>
<ul>
<li><p>An AWS account with access to AWS Secrets Manager</p>
</li>
<li><p>The AWS CLI installed and configured. Run <code>aws configure</code> and provide your access key, secret key, default region, and output format. The credentials need permission to read and write secrets in AWS Secrets Manager.</p>
</li>
<li><p><code>kubectl</code> installed. For Microk8s, run <code>microk8s kubectl config view --raw &gt; ~/.kube/config</code> after installation to connect kubectl to your local cluster.</p>
</li>
<li><p>Terraform installed</p>
</li>
<li><p>Helm installed</p>
</li>
<li><p>Docker installed</p>
</li>
<li><p>A local Kubernetes cluster: the lab supports Microk8s and kind. If you do not have either installed, follow the <a href="https://microk8s.io/">Microk8s install guide</a> before continuing.</p>
</li>
</ul>
<p><strong>For the Amazon Elastic Kubernetes Service sections:</strong></p>
<ul>
<li><p>An Amazon Elastic Kubernetes Service cluster you can create or manage</p>
</li>
<li><p>A GitHub repository you can configure for workflows and secrets</p>
</li>
</ul>
<p>The lab repository includes two deployment paths: a local path for fast learning and an Amazon Elastic Kubernetes Service path for a production-like setup. All the exact commands for each path live in the repo's <a href="https://github.com/Osomudeya/k8s-secret-lab/blob/main/docs/DEPLOY-LOCAL.md"><code>docs/DEPLOY-LOCAL.md</code></a> and <a href="https://github.com/Osomudeya/k8s-secret-lab/blob/main/docs/DEPLOY-EKS.md"><code>docs/DEPLOY-EKS.md</code></a>.</p>
<h2 id="heading-how-to-understand-the-secret-flow">How to Understand the Secret Flow</h2>
<p>Before you run any command, you need to understand how the pieces connect.</p>
<p>The flow has four stages:</p>
<ol>
<li><p>A developer or automated system updates a secret in AWS Secrets Manager.</p>
</li>
<li><p>The External Secrets Operator polls AWS Secrets Manager on a schedule and creates or updates a Kubernetes Secret.</p>
</li>
<li><p>Your pod reads that Kubernetes Secret.</p>
</li>
<li><p>During rotation, the Kubernetes Secret updates, but your two consumption modes behave differently.</p>
</li>
</ol>
<img src="https://cdn.hashnode.com/uploads/covers/698d563262d4ce66226a844a/9dc52f99-add4-490a-ad86-25a30d0ae306.png" alt="A step-by-step flow diagram showing the four stages of secret flow above" style="display: block;" width="1536" height="1024" loading="lazy">

<h3 id="heading-how-the-external-secrets-operator-sync-works">How the External Secrets Operator Sync Works</h3>
<p>The External Secrets Operator reads a custom Kubernetes resource called <code>ExternalSecret</code>. That resource tells the operator three things:</p>
<ul>
<li><p>Which secret store to connect to</p>
</li>
<li><p>Which Kubernetes Secret name to create or update</p>
</li>
<li><p>How often to refresh</p>
</li>
</ul>
<p>In this lab, the <code>ExternalSecret</code> creates a Kubernetes Secret named <code>myapp-database-creds</code>. The operator also adds a template annotation that can trigger a pod restart when the secret rotates.</p>
<h3 id="heading-how-the-app-consumes-secrets">How the App Consumes Secrets</h3>
<p>The sample application exposes three endpoints so you can validate behavior at any time.</p>
<ul>
<li><p><code>/secrets/env</code> shows what environment variables the pod sees</p>
</li>
<li><p><code>/secrets/volume</code> shows what files in the mounted secret directory look like</p>
</li>
<li><p><code>/secrets/compare</code> compares both and reports whether rotation has been detected</p>
</li>
</ul>
<p>The app checks four keys: <code>DB_USERNAME</code>, <code>DB_PASSWORD</code>, <code>DB_HOST</code>, and <code>DB_PORT</code>.</p>
<h2 id="heading-how-to-run-the-local-lab">How to Run the Local Lab</h2>
<p>The local lab gives you a fast learning loop. You can see the full pipeline working and test rotation without waiting for a cloud deployment.</p>
<h3 id="heading-step-1-clone-the-repo">Step 1: Clone the Repo</h3>
<pre><code class="language-bash">git clone https://github.com/Osomudeya/k8s-secret-lab
cd k8s-secret-lab
</code></pre>
<h3 id="heading-step-2-run-the-spin-up-script">Step 2: Run the Spin-Up Script</h3>
<pre><code class="language-bash">bash spinup.sh
</code></pre>
<p>The script will ask you to choose a local cluster type. Pick Microk8s or kind, depending on what you have installed. The script installs the External Secrets Operator via Helm, applies the Terraform configuration, and deploys the sample application.</p>
<p>If the script fails at any point, check <a href="https://github.com/Osomudeya/k8s-secret-lab/blob/main/docs/TROUBLESHOOTING.md"><code>docs/TROUBLESHOOTING.md</code></a> before retrying. The most common causes are missing AWS credentials, a misconfigured kubeconfig, or a Microk8s storage add-on that is not enabled.</p>
<h3 id="heading-important-run-the-lab-ui">Important: Run the Lab UI</h3>
<p>The lab ships with a separate guided tutorial interface that runs on your laptop. This is not the in-cluster application, it's a React-based checklist at <code>lab-ui/</code> that walks you through each concept and checkpoint as you work through the lab.</p>
<p>To start it, open a second terminal and run:</p>
<pre><code class="language-bash">cd lab-ui &amp;&amp; npm install &amp;&amp; npm run dev
</code></pre>
<img src="https://cdn.hashnode.com/uploads/covers/698d563262d4ce66226a844a/873166e9-6bff-4e56-a18d-e58b9e9a5af9.png" alt="Screenshot of npm run dev lab ui terminal" style="display: block;" width="849" height="435" loading="lazy">

<p>Then open <a href="http://localhost:5173"><code>http://localhost:5173</code></a>. You'll see a module-by-module guide covering the full flow from external secrets to rotation to CI/CD.</p>
<img src="https://cdn.hashnode.com/uploads/covers/698d563262d4ce66226a844a/5a5b220b-3f23-4c7c-8388-f2e23d122e2c.png" alt="Screenshot of The Lab UI, a guided tutorial interface that runs alongside the lab and walks you through each concept and checkpoint." style="display: block;" width="1399" height="1287" loading="lazy">

<p>Keep this terminal running alongside your lab. The Lab UI and the in-cluster app (<code>localhost:3000</code>) are two separate things, the UI guides you through the steps, the app shows you the live secrets.</p>
<h3 id="heading-step-3-access-the-application">Step 3: Access the Application</h3>
<p>Once the lab finishes, port-forward the service.</p>
<pre><code class="language-bash">kubectl port-forward svc/myapp 3000:80 -n default
</code></pre>
<p>Open <code>http://localhost:3000</code>. You should see a table showing each secret key and whether the environment variable value matches the volume mount value.</p>
<img src="https://cdn.hashnode.com/uploads/covers/698d563262d4ce66226a844a/dbe122ac-b787-40d0-96f4-4b1276bab017.png" alt="Screenshot of the running application at localhost:3000. Every row in the table should show &quot;Match ✓" style="display: block;" width="1213" height="902" loading="lazy">

<h3 id="heading-step-4-validate-that-secrets-match">Step 4: Validate That Secrets Match</h3>
<p>Run the compare endpoint directly from the terminal.</p>
<pre><code class="language-bash">curl -s http://localhost:3000/secrets/compare | python3 -m json.tool
</code></pre>
<p>When everything is working, the response will include <code>"all_match": true</code>.</p>
<h2 id="heading-how-to-inspect-the-externalsecret-and-the-application">How to Inspect the ExternalSecret and the Application</h2>
<p>At this point the lab is running. Now you'll want to inspect the manifests so you understand what each part does.</p>
<h3 id="heading-step-1-read-the-externalsecret-manifest">Step 1: Read the ExternalSecret Manifest</h3>
<p>Open <a href="https://github.com/Osomudeya/k8s-secret-lab/blob/main/k8s/aws/external-secret.yaml"><code>k8s/aws/external-secret.yaml</code></a>. Focus on these four fields:</p>
<ul>
<li><p><code>refreshInterval</code>: how often the operator polls AWS Secrets Manager</p>
</li>
<li><p><code>secretStoreRef</code>: which store the operator authenticates against</p>
</li>
<li><p><code>target</code>: the name of the Kubernetes Secret to create</p>
</li>
<li><p><code>data</code>: the mapping from AWS Secrets Manager JSON keys to Kubernetes Secret keys</p>
</li>
</ul>
<p>Here is what that mapping looks like in this lab:</p>
<pre><code class="language-yaml">spec:
  refreshInterval: 1h
  secretStoreRef:
    name: aws-secrets-manager
    kind: ClusterSecretStore
  target:
    name: myapp-database-creds
    creationPolicy: Owner
  data:
    - secretKey: DB_USERNAME
      remoteRef:
        key: prod/myapp/database
        property: username
</code></pre>
<p>The <code>property</code> field tells the operator which JSON key inside the AWS secret to extract. If your secret in AWS Secrets Manager is a JSON object, each field gets its own entry here.</p>
<p>Two fields here are worth understanding before you move on. <code>creationPolicy: Owner</code> means the operator owns the Kubernetes Secret it creates. If you delete the <code>ExternalSecret</code>, the Secret is deleted too. <code>ClusterSecretStore</code> is a cluster-scoped store, meaning any namespace in the cluster can use it. A plain <code>SecretStore</code> is namespace-scoped. For this lab, cluster-scoped is the right choice because it keeps the setup simple.</p>
<h3 id="heading-step-2-read-the-deployment-manifest">Step 2: Read the Deployment Manifest</h3>
<p>Open <a href="http://github.com/Osomudeya/k8s-secret-lab/blob/main/k8s/aws/deployment.yaml"><code>k8s/aws/deployment.yaml</code></a>. You are looking for two sections: <code>envFrom</code> and <code>volumeMounts</code>.</p>
<pre><code class="language-yaml">envFrom:
  - secretRef:
      name: myapp-database-creds

volumeMounts:
  - name: db-secret-vol
    mountPath: /etc/secrets
    readOnly: true
</code></pre>
<p>Both paths read from the same Kubernetes Secret, <code>myapp-database-creds</code>. The <code>envFrom</code> block injects all keys as environment variables at pod start.<br>The <code>volumeMounts</code> block mounts the same secret as files under <code>/etc/secrets</code>.</p>
<p>This is the core of the rotation lesson. Both paths read the same source. But they behave differently after that source changes.</p>
<h3 id="heading-step-3-read-the-app-comparison-logic">Step 3: Read the App Comparison Logic</h3>
<p>Open <a href="https://github.com/Osomudeya/k8s-secret-lab/blob/main/app/server.js"><code>app/server.js</code></a>. The comparison logic reads environment variables from <code>process.env</code> and reads mounted secret files from <code>/etc/secrets/&lt;key&gt;</code>. Then it computes a per-key match and a global <code>all_match</code> value.</p>
<p>The <code>/secrets/compare</code> endpoint sets <code>rotation_detected: true</code> when any key differs between env and volume.</p>
<h2 id="heading-how-to-test-secret-rotation">How to Test Secret Rotation</h2>
<p>Secret rotation is where real teams feel pain. This lab makes that pain visible so you can explain it clearly and fix it confidently.</p>
<h3 id="heading-how-the-rotation-gap-works"><strong>How the Rotation Gap Works</strong></h3>
<p>When a pod starts, Kubernetes gives it two ways to read a secret.</p>
<p>The first way is environment variables. Think of these like sticky notes written on the wall of the container the moment it boots up. The value gets written once, at startup, and never changes. Even if the secret in AWS gets updated ten minutes later, the sticky note still says the old value. The container cannot see the update because nobody rewrote the note.</p>
<p>The second way is a volume mount. Think of this like a shared folder that someone else can update remotely. Kubernetes creates a small folder inside the container and puts the secret value in a file there. When the secret changes in AWS and ESO syncs it into Kubernetes, the kubelet quietly updates that file within about 60 seconds. The container reads the file fresh every time it needs the value, so it sees the new password automatically.</p>
<p>Same secret, two paths. One goes stale while one stays fresh.</p>
<p>The problem happens when your app reads the database password from the environment variable, the sticky note, and someone rotates the password in AWS. ESO updates Kubernetes. The file gets the new password. But your app is still reading the sticky note, which has the old one. Connection fails.</p>
<p>That difference isn't a bug. It's how the Linux process model and the kubelet work. Understanding it is the difference between knowing Kubernetes secrets and actually operating them.</p>
<p>Here is what you're about to observe in the lab:</p>
<ul>
<li><p>The rotation script updates the secret in AWS</p>
</li>
<li><p>ESO syncs the new value into Kubernetes within seconds</p>
</li>
<li><p>The volume file updates automatically</p>
</li>
<li><p>The environment variable stays stale until the pod restarts</p>
</li>
<li><p>The <code>/secrets/compare</code> endpoint shows both values side by side so you can see the gap live</p>
</li>
</ul>
<h3 id="heading-step-1-confirm-the-lab-is-ready">Step 1: Confirm the Lab Is Ready</h3>
<p>Make sure your pod and the External Secrets Operator are both running before you start.</p>
<pre><code class="language-bash">kubectl get pods -n external-secrets
kubectl get pods -n default
</code></pre>
<p>Both should show <code>Running</code>.</p>
<h3 id="heading-step-2-run-the-rotation-test-script">Step 2: Run the Rotation Test Script</h3>
<pre><code class="language-bash">bash rotation/test-rotation.sh
</code></pre>
<p>The script performs these actions in order:</p>
<ol>
<li><p>Reads the current <code>DB_PASSWORD</code> from the volume mount at <code>/etc/secrets/DB_PASSWORD</code></p>
</li>
<li><p>Reads the current <code>DB_PASSWORD</code> from the environment variable</p>
</li>
<li><p>Updates AWS Secrets Manager with a new password using <code>put-secret-value</code></p>
</li>
<li><p>Forces an immediate ESO sync by annotating the <code>ExternalSecret</code> with <code>force-sync</code></p>
</li>
<li><p>Reads the volume value again</p>
</li>
<li><p>Reads the environment variable again</p>
</li>
</ol>
<p>After the script runs, the volume and the env var will show different values.</p>
<h3 id="heading-step-3-validate-with-the-compare-endpoint">Step 3: Validate With the Compare Endpoint</h3>
<p>Hit the compare endpoint and look at the output.</p>
<pre><code class="language-bash">curl -s http://localhost:3000/secrets/compare | python3 -m json.tool
</code></pre>
<p>You'll see something like this:</p>
<pre><code class="language-json">{
  "comparison": {
    "DB_PASSWORD": {
      "env": "old-password-value",
      "volume": "new-password-value",
      "match": false
    }
  },
  "all_match": false,
  "rotation_detected": true,
  "message": "Volume has new value; env still has old value."
}
</code></pre>
<img src="https://cdn.hashnode.com/uploads/covers/698d563262d4ce66226a844a/c4ebb09f-e605-4f68-8e12-1361d94199b2.png" alt="Rotation mismatch, the volume file updated with the new password but the env var still holds the old value from pod startup." style="display: block;" width="832" height="290" loading="lazy">

<h3 id="heading-step-4-restart-the-deployment-to-sync-env-vars">Step 4: Restart the Deployment to Sync Env Vars</h3>
<p>Env vars don't update in place. You need a pod restart so new containers start with the updated Kubernetes Secret.</p>
<pre><code class="language-bash">kubectl rollout restart deployment/myapp -n default
kubectl rollout status deployment/myapp -n default
</code></pre>
<p>Then hit <code>/secrets/compare</code> again. All rows should now show <code>"all_match": true</code>.</p>
<img src="https://cdn.hashnode.com/uploads/covers/698d563262d4ce66226a844a/0040274d-a398-408c-9486-ce0a9e527479.png" alt="After a rolling restart, new pods pick up fresh env vars and all keys match." style="display: block;" width="821" height="436" loading="lazy">

<h3 id="heading-how-to-automate-restarts-with-reloader">How to Automate Restarts With Reloader</h3>
<p>If you don't want to restart deployments manually after every rotation, you can install <a href="https://github.com/stakater/reloader"><strong>Stakater Reloader</strong></a>. It watches an annotation on the <code>Deployment</code> and triggers a rolling restart automatically when the referenced Kubernetes Secret changes. New pods start with fresh env vars, while old pods drain cleanly. The repo's local deployment guide includes the install steps.</p>
<h2 id="heading-how-to-choose-between-external-secrets-operator-and-the-csi-driver">How to Choose Between External Secrets Operator and the CSI Driver</h2>
<p>Two patterns dominate when it comes to pulling external secrets into Kubernetes: the External Secrets Operator and the <a href="https://secrets-store-csi-driver.sigs.k8s.io/">Secrets Store CSI Driver</a>.</p>
<p>Both get cloud secrets into pods, but they do it differently. Here's a plain comparison:</p>
<table>
<thead>
<tr>
<th>Feature</th>
<th>External Secrets Operator</th>
<th>Secrets Store CSI Driver</th>
</tr>
</thead>
<tbody><tr>
<td>Creates a Kubernetes Secret</td>
<td>Yes</td>
<td>No by default</td>
</tr>
<tr>
<td>Supports <code>envFrom</code></td>
<td>Yes</td>
<td>No (workaround only)</td>
</tr>
<tr>
<td>Secret stored in etcd</td>
<td>Yes (base64)</td>
<td>No, if you skip sync</td>
</tr>
<tr>
<td>Rotation</td>
<td>ESO updates the Secret, Reloader restarts pods</td>
<td>Volume file can update in place</td>
</tr>
<tr>
<td>Best for</td>
<td>Most teams. Multi-cloud, env var support</td>
<td>Security policies that prohibit secrets in etcd</td>
</tr>
</tbody></table>
<p>This lab uses the External Secrets Operator for two reasons. First, it produces a native Kubernetes Secret, which means your application and deployment patterns match standard Kubernetes workflows. Second, having both <code>envFrom</code> and a volume mount point to the same Secret makes the rotation behavior easy to observe side by side.</p>
<p>Use the CSI Driver when your security team prohibits storing secrets in etcd through a Kubernetes Secret. The driver mounts secret data directly into the pod file system without creating a Kubernetes Secret. The tradeoff is that you lose the native <code>envFrom</code> model.</p>
<h2 id="heading-how-to-deploy-the-pattern-on-amazon-elastic-kubernetes-service">How to Deploy the Pattern on Amazon Elastic Kubernetes Service</h2>
<p>The local lab is ideal for learning. The Amazon Elastic Kubernetes Service path adds the production-like pieces: IAM role-based permissions for the operator, a load balancer for the app, and a full CI/CD workflow.</p>
<h3 id="heading-step-1-prepare-terraform-and-openid-connect-access">Step 1: Prepare Terraform and OpenID Connect Access</h3>
<p>The repository includes a one-time setup guide for OpenID Connect-based access from GitHub Actions to AWS. Run these commands in the <a href="https://github.com/Osomudeya/k8s-secret-lab/tree/main/terraform/github-oidc"><code>terraform/github-oidc</code></a> folder.</p>
<pre><code class="language-bash">cd terraform/github-oidc
terraform init
terraform plan -var="github_repo=YOUR_ORG/YOUR_REPO"
terraform apply -var="github_repo=YOUR_ORG/YOUR_REPO"
terraform output role_arn
</code></pre>
<p>Copy the role ARN from the output. You'll need it in the next step.</p>
<h3 id="heading-step-2-set-the-required-environment-variable">Step 2: Set the Required Environment Variable</h3>
<p>The Amazon Elastic Kubernetes Service spin-up path needs your GitHub Actions role ARN so Terraform can grant the CI/CD runner access to the cluster.</p>
<p>To find your AWS account ID, run:</p>
<pre><code class="language-bash">aws sts get-caller-identity --query Account --output text
</code></pre>
<p>Then set the variable, replacing <code>ACCOUNT</code> with the number that command returns.</p>
<pre><code class="language-bash">export GITHUB_ACTIONS_ROLE_ARN=arn:aws:iam::ACCOUNT:role/your-github-oidc-role
</code></pre>
<h3 id="heading-step-3-run-the-spin-up-script-for-amazon-elastic-kubernetes-service">Step 3: Run the Spin-Up Script for Amazon Elastic Kubernetes Service</h3>
<pre><code class="language-bash">bash spinup.sh --cluster eks
</code></pre>
<p>When the script finishes, it prints the application URL. Open that URL in a browser and confirm that you see the same secrets table you saw locally, with all keys showing <code>Match ✓</code>.</p>
<h3 id="heading-step-4-test-rotation-on-the-deployed-app">Step 4: Test Rotation on the Deployed App</h3>
<p>After you confirm normal operation, run the rotation test the same way you did locally.</p>
<pre><code class="language-bash">bash rotation/test-rotation.sh
</code></pre>
<p>Then use <code>/secrets/compare</code> on the Amazon Elastic Kubernetes Service load balancer URL to validate behavior in the cloud environment.</p>
<p>⚠️ <strong>Cost warning:</strong> Amazon Elastic Kubernetes Service runs at approximately $0.16 per hour. When you're done with the lab, run <a href="https://github.com/Osomudeya/k8s-secret-lab/blob/main/teardown.sh"><code>bash teardown.sh</code></a> from the repo root to destroy all AWS resources and stop charges.</p>
<img src="https://cdn.hashnode.com/uploads/covers/5e1e335a7a1d3fcc59028c64/56f05ace-9ab6-4b67-ade6-a0bd1fa3962c.png" alt="Screenshot of the app running on the ALB URL, showing all keys matched" style="display: block;" width="912" height="891" loading="lazy">

<h2 id="heading-how-to-configure-github-actions-without-stored-aws-credentials">How to Configure GitHub Actions Without Stored AWS Credentials</h2>
<p>The typical CI/CD setup stores <code>AWS_ACCESS_KEY_ID</code> and <code>AWS_SECRET_ACCESS_KEY</code> in GitHub repository secrets. Those keys never rotate. Anyone with repo access can read them. When someone leaves the team, you have to revoke keys and update every workflow.</p>
<p>OpenID Connect eliminates that problem entirely.</p>
<h3 id="heading-how-openid-connect-works-for-github-actions">How OpenID Connect Works for GitHub Actions</h3>
<p>GitHub can issue a short-lived token for each workflow run. That token identifies the run: the repository, branch, and workflow name. You create an IAM role in AWS whose trust policy says: only accept requests that come from this specific GitHub repository and branch. The GitHub Actions runner exchanges that token for temporary AWS credentials via <code>AssumeRoleWithWebIdentity</code>. No long-lived keys are ever stored anywhere.</p>
<img src="https://cdn.hashnode.com/uploads/covers/698d563262d4ce66226a844a/48e72210-a669-440e-b42e-81b0c15746ec.png" alt="The full OIDC authentication flow for GitHub Actions deploying to EKS — from minting the JWT token through AssumeRoleWithWebIdentity to temporary credentials, kubeconfig retrieval, and final kubectl apply steps." style="display: block;" width="1536" height="1024" loading="lazy">

<h3 id="heading-step-1-create-the-iam-role-with-terraform">Step 1: Create the IAM Role With Terraform</h3>
<p>The <a href="https://github.com/Osomudeya/k8s-secret-lab/tree/main/terraform/github-oidc"><code>terraform/github-oidc</code></a> folder creates the OpenID Connect provider and the IAM role for you. You already ran this in the Amazon Elastic Kubernetes Service setup above. The role ARN is the only value you need to store.</p>
<h3 id="heading-step-2-add-the-role-arn-to-github-repository-secrets">Step 2: Add the Role ARN to GitHub Repository Secrets</h3>
<p>In your GitHub repository:</p>
<ol>
<li><p>Go to Settings → Secrets and variables → Actions</p>
</li>
<li><p>Click New repository secret</p>
</li>
<li><p>Name it <code>AWS_ROLE_ARN</code></p>
</li>
<li><p>Paste the role ARN from the Terraform output</p>
</li>
</ol>
<p>That is the only secret you store. The role ARN isn't sensitive. It's an identifier, not a credential.</p>
<h3 id="heading-step-3-configure-terraform-state">Step 3: Configure Terraform State</h3>
<p>For CI/CD to work consistently across runs, Terraform needs a shared state backend. The lab stores Terraform state in an Amazon S3 bucket and uses an Amazon DynamoDB table for state locking. The Amazon Elastic Kubernetes Service deployment guide in the repo covers the backend setup in full.</p>
<h3 id="heading-step-4-push-to-main-and-let-workflows-run">Step 4: Push to Main and Let Workflows Run</h3>
<p>After your first spin-up, every push to the <code>main</code> branch drives the CI/CD pipeline. The repo includes separate workflow files for Terraform infrastructure changes and application deployment changes. Once your application is reachable, use <code>/secrets/compare</code> to validate rotation behavior on the live environment.</p>
<h2 id="heading-how-to-troubleshoot-the-most-common-failures">How to Troubleshoot the Most Common Failures</h2>
<p>Here's a shortlist of the most common symptoms and their fixes.</p>
<table>
<thead>
<tr>
<th>Symptom</th>
<th>Most Likely Cause</th>
<th>Fix</th>
</tr>
</thead>
<tbody><tr>
<td><code>ExternalSecret</code> is not syncing</td>
<td>Missing credentials or wrong store reference</td>
<td>Confirm the operator can access AWS Secrets Manager and that <code>secretStoreRef</code> points to the correct store</td>
</tr>
<tr>
<td>Pod is stuck in <code>Pending</code></td>
<td>Missing storage setup for local cluster</td>
<td>For Microk8s, enable the storage add-on</td>
</tr>
<tr>
<td>Env and volume still match after rotation</td>
<td>Rotation happened but the pod never restarted</td>
<td>Run <code>kubectl rollout restart</code> or install Reloader</td>
</tr>
<tr>
<td>CRD or API version mismatch</td>
<td>ESO version and manifest <code>apiVersion</code> don't match</td>
<td>Verify the <code>apiVersion</code> for <code>ClusterSecretStore</code> and <code>ExternalSecret</code> match your installed ESO version</td>
</tr>
<tr>
<td>Amazon Elastic Kubernetes Service node group never joins</td>
<td>Networking or IAM permissions for nodes are wrong</td>
<td>Fix internet routing and review the node IAM policy</td>
</tr>
</tbody></table>
<h3 id="heading-how-to-inspect-the-operator-and-the-externalsecret">How to Inspect the Operator and the ExternalSecret</h3>
<p>When something isn't syncing, start with these two commands.</p>
<pre><code class="language-bash"># Check the ExternalSecret status
kubectl describe externalsecret app-db-secret -n default

# Check the ESO operator logs
kubectl logs -n external-secrets -l app.kubernetes.io/name=external-secrets
</code></pre>
<p>The status conditions on the <code>ExternalSecret</code> resource will usually tell you exactly what failed.</p>
<h3 id="heading-how-to-validate-rotation-from-the-app-side">How to Validate Rotation From the App Side</h3>
<p>When you are debugging rotation, don't rely only on Kubernetes resource state. Use the <code>/secrets/compare</code> endpoint to see what the running application actually observes. The endpoint tells you whether env and volume match and whether rotation has been detected. That is the ground truth for your application's behavior.</p>
<h2 id="heading-conclusion">Conclusion</h2>
<p>You now have a complete secrets pipeline from AWS Secrets Manager into Kubernetes pods using Terraform and the External Secrets Operator. You ran the local lab, inspected the <code>ExternalSecret</code> and <code>Deployment</code> manifests, and validated that the application sees the right credentials.</p>
<p>You also tested secret rotation and observed the key behavior firsthand: mounted secret files update within the kubelet sync period, while environment variables stay stale until the pod restarts. That single observation explains a large class of production incidents.</p>
<p>Finally, you saw how the same design extends to Amazon Elastic Kubernetes Service with OpenID Connect-based CI/CD, and you have a troubleshooting checklist for the failures most teams hit.</p>
<p>The lab repository is at <a href="https://github.com/Osomudeya/k8s-secret-lab">github.com/Osomudeya/k8s-secret-lab</a>. If you ran the local lab, the natural next step is phases 4 and 5 from the repo's staged learning path: try the CSI driver path on Microk8s, then follow the EKS setup to see the same pipeline with a real CI/CD workflow and no credentials stored in GitHub. Both are documented in the repo and take less than 30 minutes each.</p>
<p>If this helped you, star the repo and share it with someone who is learning Kubernetes.</p>
<p><em>I send weekly breakdowns of real production incidents and how engineers actually fix them, not tutorials but real failures<br>→</em> <a href="https://osomudeya.gumroad.com/subscribe"><em>Join the newsletter</em></a></p>
 ]]>
                </content:encoded>
            </item>
        
            <item>
                <title>
                    <![CDATA[ Penetration Testing — Services vs Automated Platforms: What’s Better in 2026? ]]>
                </title>
                <description>
                    <![CDATA[ In 2026, cybersecurity teams face more threats than ever before. Attack surfaces are broad, technology stacks are complex, and adversaries are quick to exploit weak points. Against this backdrop, comp ]]>
                </description>
                <link>https://www.freecodecamp.org/news/penetration-testing-services-vs-automated-platforms-what-is-better/</link>
                <guid isPermaLink="false">69b843d22ad6ae5184d73e34</guid>
                
                    <category>
                        <![CDATA[ Security ]]>
                    </category>
                
                    <category>
                        <![CDATA[ cybersecurity ]]>
                    </category>
                
                    <category>
                        <![CDATA[ pentesting ]]>
                    </category>
                
                    <category>
                        <![CDATA[ automation ]]>
                    </category>
                
                <dc:creator>
                    <![CDATA[ Manish Shivanandhan ]]>
                </dc:creator>
                <pubDate>Mon, 16 Mar 2026 17:54:26 +0000</pubDate>
                <media:content url="https://cdn.hashnode.com/uploads/covers/5e1e335a7a1d3fcc59028c64/820ccff8-9ef7-4b12-a7a9-113c5a71abdc.png" medium="image" />
                <content:encoded>
                    <![CDATA[ <p>In 2026, cybersecurity teams face more threats than ever before.</p>
<p>Attack surfaces are broad, technology stacks are complex, and adversaries are quick to exploit weak points.</p>
<p>Against this backdrop, companies must decide how best to test their defences.</p>
<p>Two main approaches have emerged as leaders: human-led penetration testing services and automated testing platforms. Each has strengths and limitations. Choosing the right one depends on your security goals, risk tolerance, and budget.</p>
<p>At its core, <a href="https://www.cloudflare.com/learning/security/glossary/what-is-penetration-testing/">penetration testing</a> is about finding security holes before attackers do. But how you get there matters.</p>
<p>Human experts bring creativity and real-world insight, while automated platforms offer scale and speed.</p>
<p>This article explores both approaches and compares top providers to help you decide what’s better for your organization in 2026.</p>
<h3 id="heading-what-well-cover">What we'll cover:</h3>
<ol>
<li><p><a href="#heading-what-are-penetration-testing-services">What Are Penetration Testing Services?</a></p>
</li>
<li><p><a href="#heading-what-are-automated-penetration-testing-platforms">What Are Automated Penetration Testing Platforms?</a></p>
</li>
<li><p><a href="#heading-why-the-debate-matters-in-2026">Why the Debate Matters in 2026</a></p>
<ul>
<li><p><a href="#heading-depth-of-testing-humans-vs-machines">Depth of Testing: Humans vs Machines</a></p>
</li>
<li><p><a href="#heading-speed-and-frequency-of-testing">Speed and Frequency of Testing</a></p>
</li>
<li><p><a href="#heading-cost-considerations">Cost Considerations</a></p>
</li>
<li><p><a href="#heading-integration-with-security-workflows">Integration with Security Workflows</a></p>
</li>
</ul>
</li>
<li><p><a href="#heading-real-world-context-top-providers-in-2026">Real World Context: Top Providers in 2026</a></p>
</li>
<li><p><a href="#heading-compliance-and-reporting">Compliance and Reporting</a></p>
</li>
<li><p><a href="#heading-which-one-should-you-choose-in-2026">Which One Should You Choose in 2026?</a></p>
</li>
<li><p><a href="#heading-final-thoughts">Final Thoughts</a></p>
</li>
</ol>
<h2 id="heading-what-are-penetration-testing-services">What Are Penetration Testing Services?</h2>
<p>Penetration testing services are engagements where cybersecurity professionals actively probe your systems to find vulnerabilities. These experts use a mix of tools, manual techniques, and real-world attack simulations to surface weaknesses that machines might miss.</p>
<p>These services may include scheduled tests, one-time assessments, and ongoing engagements. Many providers tailor their approach to the environment being tested, whether that’s a corporate network, web application, cloud infrastructure, or mobile ecosystem.</p>
<p>Human testers think like attackers, combining automated scans with logic and adaptability that machines cannot replicate on their own.</p>
<p>These engagements are typically measured in reports, debrief sessions, and clear remediation guidance. The human element is the defining factor. A skilled tester doesn’t just find flaws. They understand context, creative exploit paths, and business impact.</p>
<h2 id="heading-what-are-automated-penetration-testing-platforms">What Are Automated Penetration Testing Platforms?</h2>
<p>Automated penetration testing platforms use software to scan, crawl, and test systems for vulnerabilities. These platforms run scheduled scans or continuous assessments with minimal human intervention. They aim to find flaws early and often, integrating with development pipelines or security operations centers.</p>
<p>Automation brings consistency, speed, and the ability to repeat tests frequently. Many modern platforms use machine learning to prioritize findings and reduce noise. Some offer automation rules that trigger scans based on changes in the environment or codebase.</p>
<p>In contrast to full manual services, platforms are best suited for ongoing baseline assessments and rapid feedback. They are often priced in subscription models and integrate with other tooling like bug tracking systems or <a href="https://www.ibm.com/think/topics/siem">SIEMs</a>. While they can pinpoint known vulnerability patterns efficiently, automated tools are limited in creative attack paths and logic-based exploits.</p>
<h2 id="heading-why-the-debate-matters-in-2026">Why the Debate Matters in&nbsp;2026</h2>
<p>In 2026, the cybersecurity landscape is both more advanced and more hazardous. Organizations operate hybrid clouds, microservices architectures, and complex supply chains.</p>
<p>Threat actors are using AI to scale attacks. In this environment, the question is not only about finding old vulnerabilities but anticipating novel attack methods.</p>
<p>With limited resources, security leaders must choose wisely. Do you invest heavily in services with human experts? Do you adopt automated platforms that test continuously?</p>
<p>Maybe a mix is best. To answer these questions, let’s explore how the two approaches compare across key criteria.</p>
<h3 id="heading-depth-of-testing-humans-vs-machines">Depth of Testing: Humans vs&nbsp;Machines</h3>
<p>Human-led penetration tests shine when deep context and logic are required. Expert testers can chain together multiple issues to compromise a system in ways automated tools don't anticipate. They explore paths, think creatively, and adapt in real time to the environment they encounter.</p>
<p>Automated platforms excel at breadth and repetition. They perform wide sweeps of systems quickly and can generate alerts on common vulnerability classes. They're particularly strong in repetitive tasks like scanning hundreds of endpoints or validating compliance controls.</p>
<p>But platforms often rely on predefined signatures and patterns. They perform poorly when an exploit requires intuition or lateral thinking.</p>
<p>In simple terms, human services dig deep while platforms dig wide.</p>
<h3 id="heading-speed-and-frequency-of-testing">Speed and Frequency of&nbsp;Testing</h3>
<p>Automated platforms have a clear advantage in speed and frequency. They can run multiple scans in parallel, test after every code commit, and provide almost immediate feedback. This makes them ideal for DevOps pipelines and agile environments that change daily.</p>
<p>Penetration testing services, by design, occur on a schedule. A quarterly or annual test may be thorough, but it cannot match the cadence that automated tools provide.</p>
<p>Manual tests take time to plan, execute, and analyze. In fast-moving environments, this might leave gaps between testing windows.</p>
<p>For many organizations, automation fills these gaps, while manual testing provides periodic, deep insight.</p>
<h3 id="heading-cost-considerations">Cost Considerations</h3>
<p>Cost is always a factor. Automated platforms generally come with lower upfront costs compared to human-led engagements. Subscriptions scale with usage and provide continuous assessment for a predictable price. This makes them appealing to midsize companies or teams with limited budgets.</p>
<p>Penetration testing services, especially from reputable consultancies, command higher fees. These reflect labor costs, expertise, and the bespoke nature of the work.</p>
<p>However, the value gained is often more than just flaw detection: it’s expert interpretation, custom exploitation paths, and strategic guidance.</p>
<p>In cost-benefit terms, automated platforms provide the most value per dollar for baseline security, while services deliver high-value insight that can justify a higher cost.</p>
<h3 id="heading-integration-with-security-workflows">Integration with Security Workflows</h3>
<p>Automated platforms are built to integrate with broader security tooling. They often connect to continuous integration/continuous delivery (CI/CD) pipelines, vulnerability management platforms, and ticketing systems. This integration ensures that issues are communicated to the teams who need them most and tracked to resolution.</p>
<p>Penetration testing services can integrate into workflows too, but this usually requires additional coordination. Reports must be ingested into tracking systems and aligned with internal priorities. Some providers offer APIs and extended services that help bridge this gap, but the process typically takes more effort than with automated platforms.</p>
<p>Integration matters because security cannot operate in isolation. Automated platforms fit more naturally into modern DevSecOps workflows, while services provide episodic insights that must be planned and bridged into operations.</p>
<h2 id="heading-real-world-context-top-providers-in-2026">Real World Context: Top Providers in&nbsp;2026</h2>
<p>To illustrate how these approaches manifest in practice, consider a few leading options. Each provider offers different strengths in manual services or automated tooling.</p>
<p>One such provider is <a href="https://xbow.com/pentest">XBOW</a>. XBOW is known for deep manual testing engagements, combining expert human testers with structured methodologies across network, application, and cloud environments. Their work emphasizes real-world attack simulations and strategic risk reporting.</p>
<p>Another well-known provider is <a href="https://www.cobalt.io/">Cobalt</a>. Cobalt blends human expertise with platform-based management. Their Pentest as a Service (PtaaS) model connects testers to client environments through a platform that organizes findings, workflows, and communication. Clients can collaborate with testers, track issues in real time, and integrate results with other systems.</p>
<p>A different model comes from <a href="https://www.synack.com/">Synack</a>. Synack uses a crowd of vetted testers who work with a secure testing platform. This hybrid model aims to combine the creativity of human testers with the scalability and tracking of automated systems. Clients benefit from diverse testing styles and coordinated reporting within a structured platform.</p>
<p>Each of these approaches has merit. Some lean more toward pure services, others toward platform-driven collaboration. Your choice should align with your security maturity and goals.</p>
<h2 id="heading-compliance-and-reporting">Compliance and Reporting</h2>
<p>For regulated industries, compliance matters. Automated platforms often include reporting features that map directly to standards like PCI DSS, HIPAA, or ISO 27001. These reports can be generated on a regular cadence and integrated into audit evidence.</p>
<p>Penetration testing services provide compliance support too, but the reports are typically narrative and bespoke. The real value is in expert interpretation of compliance requirements and guidance on remediating complex findings.</p>
<p>In essence, automation provides structured, repeatable reporting, while services deliver customized insights that may carry more weight with auditors and internal stakeholders.</p>
<h2 id="heading-which-one-should-you-choose-in-2026">Which One Should You Choose in&nbsp;2026?</h2>
<p>There is no one-size-fits-all answer. Many organizations adopt both approaches. Automated platforms serve as the first line of defense by continuously scanning for known issues and tracking progress over time. Human-led services then provide a deeper second layer, uncovering complex issues and offering strategic guidance.</p>
<p>If your environment is highly dynamic, with frequent releases and evolving infrastructure, an automated platform is essential. If you operate in a high-risk sector where attackers are likely to craft bespoke exploits, human-led penetration testing services are indispensable.</p>
<p>Most mature security programs use both. Automation drives frequency and scale. Human services provide depth and insight. Together, they form a layered testing strategy that maximizes coverage and minimizes blind spots.</p>
<h2 id="heading-final-thoughts">Final Thoughts</h2>
<p>In 2026, cybersecurity testing is more sophisticated and essential than ever. Organizations must balance speed, depth, cost, and context when selecting between penetration testing services and automated platforms. While one is not inherently better than the other in all cases, understanding their differences and complementary strengths will help you build a robust security posture.</p>
<p>Automated platforms catch the routine and repetitive, giving continuous visibility into known risks. Human-led services uncover the hidden and unexpected, thinking beyond patterns to simulate real adversaries. For most teams, the future of testing lies in a hybrid approach that leverages both.</p>
<p>By aligning your security goals with the right mix of services and tools, you can stay ahead of threats now and in the years to come.</p>
<p><em>Hope you enjoyed this article. Learn more about me by</em> <a href="https://manishmshiva.me"><em><strong>visiting my website</strong></em></a><em>.</em></p>
 ]]>
                </content:encoded>
            </item>
        
            <item>
                <title>
                    <![CDATA[ What is Disaster Recovery Testing? Explained with Practical Examples ]]>
                </title>
                <description>
                    <![CDATA[ Most teams are confident they can recover from a major outage until they actually have to. Backups exist, architectures are redundant and a recovery plan is documented somewhere, yet real incidents of ]]>
                </description>
                <link>https://www.freecodecamp.org/news/disaster-recovery-testing/</link>
                <guid isPermaLink="false">69a5614ffc6453a5f17ca809</guid>
                
                    <category>
                        <![CDATA[ Testing ]]>
                    </category>
                
                    <category>
                        <![CDATA[ cybersecurity ]]>
                    </category>
                
                    <category>
                        <![CDATA[ Databases ]]>
                    </category>
                
                    <category>
                        <![CDATA[ Security ]]>
                    </category>
                
                <dc:creator>
                    <![CDATA[ Alex Tray ]]>
                </dc:creator>
                <pubDate>Mon, 02 Mar 2026 10:07:11 +0000</pubDate>
                <media:content url="https://cdn.hashnode.com/uploads/covers/5fc16e412cae9c5b190b6cdd/57c1e51b-867c-444e-90f0-e6551284fe0a.png" medium="image" />
                <content:encoded>
                    <![CDATA[ <p>Most teams are confident they can recover from a major outage until they actually have to. Backups exist, architectures are redundant and a recovery plan is documented somewhere, yet real incidents often reveal critical gaps.</p>
<p>Disaster recovery testing is what separates assumed resilience from proven recovery, but it’s still skipped, rushed or treated as a checkbox exercise. For developers and technical teams, that gap can turn a manageable failure into a prolonged outage.</p>
<h2 id="heading-table-of-contents">Table of Contents</h2>
<ul>
<li><p><a href="#heading-what-is-disaster-recovery-testing">What is Disaster Recovery Testing?</a></p>
</li>
<li><p><a href="#heading-how-disaster-recovery-testing-works-in-practice">How Disaster Recovery Testing Works in Practice</a></p>
</li>
<li><p><a href="#heading-disaster-recovery-testing-methods-developers-should-know">Disaster Recovery Testing Methods Developers Should Know</a></p>
</li>
<li><p><a href="#heading-what-technology-disaster-recovery-testing-evaluates">What Technology Disaster Recovery Testing Evaluates</a></p>
</li>
<li><p><a href="#heading-how-to-test-a-disaster-recovery-plan">How to Test a Disaster Recovery Plan</a></p>
</li>
<li><p><a href="#heading-disaster-recovery-test-scenarios-practical-examples">Disaster Recovery Test Scenarios: Practical Examples</a></p>
</li>
<li><p><a href="#heading-disaster-recovery-test-report-turning-tests-into-improvements">Disaster Recovery Test Report: Turning Tests Into Improvements</a></p>
</li>
<li><p><a href="#heading-disaster-recovery-audits-and-continuous-validation">Disaster Recovery Audits and Continuous Validation</a></p>
</li>
<li><p><a href="#heading-conclusion">Conclusion</a></p>
</li>
</ul>
<h2 id="heading-what-is-disaster-recovery-testing"><strong>What is Disaster Recovery Testing?</strong></h2>
<p>Disaster recovery (DR) testing is the process of validating that systems, data and applications can be restored after a disruptive event within defined recovery objectives. It generally evaluates:</p>
<ul>
<li><p><strong>Recovery Time Objective (RTO)</strong>: How quickly systems must be restored.</p>
</li>
<li><p><strong>Recovery Point Objective (RPO)</strong>: How much data loss is acceptable.</p>
</li>
<li><p><strong>Operational readiness</strong>: Whether teams know what to do during an incident.</p>
</li>
</ul>
<p>A disaster recovery test plan documents how these elements are tested, who is responsible and what success looks like. Without testing, DR plans are assumptions, not guarantees.</p>
<h2 id="heading-how-disaster-recovery-testing-works-in-practice"><strong>How Disaster Recovery Testing Works in Practice</strong></h2>
<p>In real environments, disaster recovery testing is used to check all <a href="https://www.nakivo.com/blog/components-disaster-recovery-plan-checklist/">elements of the disaster recovery plan</a> and is rarely a single event. It’s a structured exercise that simulates failure, observes system behavior and measures outcomes against expectations.</p>
<p>A typical DR test involves:</p>
<ol>
<li><p><strong>Defining scope</strong> – Which applications, services, or data sets are included.</p>
</li>
<li><p><strong>Selecting a scenario</strong> – Outage, corruption, ransomware, region failure, and so on.</p>
</li>
<li><p><strong>Executing recovery actions</strong> – Restore data, fail over systems, reconfigure dependencies.</p>
</li>
<li><p><strong>Measuring results</strong> – Time to recovery, data consistency, service availability.</p>
</li>
<li><p><strong>Documenting findings</strong> – What worked, what failed, what needs improvement.</p>
</li>
</ol>
<p>For developers, the key shift is recognizing that DR testing isn’t just an ops exercise. Application architecture, data handling and deployment patterns all influence recovery outcomes.</p>
<p>Importantly, regulatory pressure is also reshaping how organizations approach recovery validation. Frameworks such as the <a href="https://heimdalsecurity.com/nis-2-directive">NIS2 Directive</a> require essential and important entities in the EU to implement robust cybersecurity risk management measures, including incident response and business continuity capabilities.</p>
<h2 id="heading-disaster-recovery-testing-methods-developers-should-know"><strong>Disaster Recovery Testing Methods Developers Should Know</strong></h2>
<p>Different testing methods provide different levels of confidence. Mature teams use more than one. Each method has a place, but relying only on low-impact testing creates blind spots that surface during real incidents.</p>
<h3 id="heading-checklist-testing"><strong>Checklist Testing</strong></h3>
<p>The simplest method: Teams review documented recovery steps without executing them. This helps validate documentation completeness but does not confirm real-world recoverability.</p>
<h3 id="heading-tabletop-exercises"><strong>Tabletop Exercises</strong></h3>
<p>Stakeholders walk through a simulated disaster scenario and discuss responses. Tabletop tests are useful for identifying communication gaps and unclear responsibilities, especially for cross-team coordination.</p>
<h3 id="heading-partial-or-component-testing"><strong>Partial or Component Testing</strong></h3>
<p>Specific systems, such as databases or backup restores, are tested in isolation. Developers often encounter this when validating recovery procedures for individual services or environments.</p>
<h3 id="heading-full-scale-testing"><strong>Full-scale Testing</strong></h3>
<p>This is the most comprehensive method. It involves actual failover or full recovery in production-like environments. While disruptive, full-scale tests provide the highest confidence.</p>
<h2 id="heading-what-technology-disaster-recovery-testing-evaluates"><strong>What Technology Disaster Recovery Testing Evaluates</strong></h2>
<p>Modern environments are complex, and disaster recovery testing must validate more than just data restores.</p>
<p>DR testing evaluates:</p>
<ul>
<li><p><strong>Backup integrity</strong> – Are backups usable, consistent and complete?</p>
</li>
<li><p><strong>Application dependencies</strong> – Do services come back in the correct order?</p>
</li>
<li><p><strong>Infrastructure recovery</strong> – Can compute, storage and networking be re-provisioned?</p>
</li>
<li><p><strong>Identity and access</strong> – Do credentials, secrets and permissions still function?</p>
</li>
<li><p><strong>Automation and scripts</strong> – Do recovery workflows still match current architectures?</p>
</li>
</ul>
<p>For developers, this often reveals hidden coupling between services, outdated scripts or environment-specific assumptions that were never documented.</p>
<h2 id="heading-how-to-test-a-disaster-recovery-plan"><strong>How to Test a Disaster Recovery Plan</strong></h2>
<p>Testing a disaster recovery plan doesn’t require shutting down production on day one. A practical, incremental approach works best.</p>
<ol>
<li><p><strong>Start with a single application</strong>: Pick a service with well-defined data and dependencies. Avoid starting with your most complex system.</p>
</li>
<li><p><strong>Validate backup restores</strong>: Restore data into a non-production environment and confirm application functionality, not just file presence.</p>
</li>
<li><p><strong>Measure RTO and RPO</strong>: Time the recovery process and compare results to stated objectives. At this stage, many teams can discover that their objectives were unrealistic.</p>
</li>
<li><p><strong>Test failure assumptions</strong>: Simulate real-world issues like missing credentials, expired certificates or partial data loss.</p>
</li>
<li><p><strong>Document gaps immediately</strong>: Update the disaster recovery test plan while findings are fresh. Untested fixes are just new assumptions.</p>
</li>
</ol>
<p>This approach makes disaster recovery testing part of standard processes rather than a once-a-year compliance task.</p>
<h3 id="heading-automating-restore-validation"><strong>Automating Restore Validation</strong></h3>
<p>One of the most common gaps in disaster recovery testing is stopping at “restore completed” instead of validating that the application actually works. A restored database that can’t serve queries or contains incomplete data doesn’t meet recovery objectives.</p>
<p>Teams can reduce this risk by automating post-restore validation. For example, after restoring a PostgreSQL database into a staging or isolated DR environment, a simple validation script can confirm connectivity and basic data integrity:</p>
<pre><code class="language-python">import psycopg2

import sys


def validate_restore():

&nbsp;&nbsp;&nbsp;&nbsp;try:

&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;conn = psycopg2.connect(

&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;host="restored-db.internal",

&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;database="appdb",

&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;user="dr_test_user",

&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;password="securepassword"

&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;)

&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;cur = conn.cursor()

&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;cur.execute("SELECT COUNT(*) FROM users;")

&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;result = cur.fetchone()



&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;if result and result[0] &gt; 0:

&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;print("Restore validation successful.")

&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;else:

&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;print("Restore validation failed: No data found.")

&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;sys.exit(1)


&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;conn.close()

&nbsp;&nbsp;&nbsp;&nbsp;except Exception as e:

&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;print(f"Restore validation error: {e}")

&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;sys.exit(1)


validate_restore()
</code></pre>
<p>This script does three important things:</p>
<ul>
<li><p>Confirms the database is reachable</p>
</li>
<li><p>Executes a real query, not just a connection check</p>
</li>
<li><p>Fails explicitly if the expected data is missing</p>
</li>
</ul>
<p>In practice, teams can integrate scripts like this into CI/CD pipelines or scheduled recovery drills. The goal isn’t to test every edge case, but to move from “backup exists” to “restore is functionally verified.” Over time, these automated checks become part of the disaster recovery test plan, helping teams measure RTO accurately and detect configuration drift before a real incident exposes it.</p>
<h2 id="heading-disaster-recovery-test-scenarios-practical-examples"><strong>Disaster Recovery Test Scenarios: Practical Examples</strong></h2>
<p>Effective disaster recovery testing focuses on realistic failures, not idealized outages.</p>
<h3 id="heading-accidental-deletion-or-misconfiguration"><strong>Accidental Deletion or Misconfiguration</strong></h3>
<p>A dropped database table, deleted storage bucket or bad configuration change tests how quickly teams can restore specific data without rolling back entire systems. These everyday incidents often reveal slow or overly manual recovery processes.</p>
<h3 id="heading-data-corruption-and-application-failure"><strong>Data Corruption and Application Failure</strong></h3>
<p>Buggy releases can silently corrupt data while systems remain online. This scenario validates point-in-time recovery and whether teams can identify when corruption started, not just restore the latest backup.</p>
<h3 id="heading-ransomware-simulation"><strong>Ransomware Simulation</strong></h3>
<p>Ransomware testing checks whether clean, uncompromised backups can be restored in isolation. It often exposes gaps in backup immutability, credential handling and realistic recovery times.</p>
<h3 id="heading-infrastructure-or-platform-outage"><strong>Infrastructure or Platform Outage</strong></h3>
<p>Simulating the loss of a cluster, availability zone or region tests automation and infrastructure-as-code maturity. In virtualized environments, most commonly <a href="https://www.nakivo.com/vmware-disaster-recovery/">VMware disaster recovery</a>, testing involves restoring virtual machines at a secondary site and validating networking and application dependencies.</p>
<h3 id="heading-credential-and-access-failure"><strong>Credential and Access Failure</strong></h3>
<p>Recovery can stall if credentials, certificates or secret keys are unavailable. Testing this scenario validates identity systems and whether recovery procedures rely on fragile access assumptions.</p>
<h2 id="heading-disaster-recovery-test-report-turning-tests-into-improvements"><strong>Disaster Recovery Test Report: Turning Tests Into Improvements</strong></h2>
<p>Testing without documentation is wasted effort. A disaster recovery test report turns results into actionable improvements.</p>
<p>A valuable DR test report includes:</p>
<ul>
<li><p>Test scope and scenario</p>
</li>
<li><p>Expected vs. actual RTO/RPO</p>
</li>
<li><p>Recovery steps executed</p>
</li>
<li><p>Failures, delays and root causes</p>
</li>
<li><p>Recommended changes</p>
</li>
</ul>
<p>For developers, this often results in concrete action items: refactoring startup dependencies, adding health checks, improving automation or adjusting data protection policies. The report should feed directly into backlog planning.</p>
<h2 id="heading-disaster-recovery-audits-and-continuous-validation"><strong>Disaster Recovery Audits and Continuous Validation</strong></h2>
<p>Audits often expose what teams already suspect: Disaster recovery plans exist, but haven’t been tested recently (or at all).</p>
<p>Rather than treating audits as one-time events, teams should adopt continuous validation:</p>
<ul>
<li><p>Regular restore tests integrated into CI/CD pipelines.</p>
</li>
<li><p>Scheduled DR tests tied to major architecture changes.</p>
</li>
<li><p>Automated alerts when recovery objectives drift.</p>
</li>
</ul>
<p>This shifts disaster recovery testing from an annual obligation to an ongoing practice that evolves alongside the environment.</p>
<h2 id="heading-conclusion"><strong>Conclusion</strong></h2>
<p>Disaster recovery testing is not about pessimism, it’s about realism. Systems and people change, and failure modes evolve faster than documentation. Without testing, even the best-designed recovery plan can become outdated.</p>
<p>For developers and technical teams, practicing disaster recovery testing builds confidence rooted in evidence, not assumptions. It exposes hidden dependencies, validates data protection strategies and ensures that when something goes wrong, recovery is predictable instead of chaotic.</p>
 ]]>
                </content:encoded>
            </item>
        
            <item>
                <title>
                    <![CDATA[ Learn Cybersecurity and Ethical Hacking using Kali Linux ]]>
                </title>
                <description>
                    <![CDATA[ To protect your applications against threats, it’s helpful to understand the methods that attackers can use against you. We just posted a course on the freeCodeCamp.org YouTube channel that will help you master the fundamentals of cybersecurity and e... ]]>
                </description>
                <link>https://www.freecodecamp.org/news/learn-cybersecurity-and-ethical-hacking-using-kali-linux/</link>
                <guid isPermaLink="false">698ca68cf15922839824f638</guid>
                
                    <category>
                        <![CDATA[ Ethical Hacking ]]>
                    </category>
                
                    <category>
                        <![CDATA[ cybersecurity ]]>
                    </category>
                
                    <category>
                        <![CDATA[ youtube ]]>
                    </category>
                
                <dc:creator>
                    <![CDATA[ Beau Carnes ]]>
                </dc:creator>
                <pubDate>Wed, 11 Feb 2026 15:55:56 +0000</pubDate>
                <media:content url="https://cdn.hashnode.com/res/hashnode/image/upload/v1770825132682/2e29f195-76dc-4def-8931-fc86f409e131.jpeg" medium="image" />
                <content:encoded>
                    <![CDATA[ <p>To protect your applications against threats, it’s helpful to understand the methods that attackers can use against you.</p>
<p>We just posted a course on the freeCodeCamp.org YouTube channel that will help you master the fundamentals of cybersecurity and ethical hacking. This is a beginner-friendly, hands-on course using Kali Linux.</p>
<p>You’ll learn to identify, exploit, and defend against real-world vulnerabilities while building a solid foundation in penetration testing, network security, and vulnerability assessment. By mastering professional tools like Nmap and Wireshark, you will develop the practical skills and mindset needed to secure systems and think like an ethical hacker. This course was created by Sunny Dimalu The Cyborg.</p>
<p>Here are the sections in this course:</p>
<ul>
<li><p>Introduction</p>
</li>
<li><p>What is Kali Linux</p>
</li>
<li><p>Basic Commands &amp; Terminal Customization</p>
</li>
<li><p>ls Command</p>
</li>
<li><p>cd Command</p>
</li>
<li><p>Nano Editor</p>
</li>
<li><p>cat Command</p>
</li>
<li><p>Create Files Using cat</p>
</li>
<li><p>Create Directories</p>
</li>
<li><p>grep Command</p>
</li>
<li><p>wc Command</p>
</li>
<li><p>Output Redirection</p>
</li>
<li><p>Piping</p>
</li>
<li><p>Copy Files</p>
</li>
<li><p>Remove Files &amp; Directories</p>
</li>
<li><p>Types of Users</p>
</li>
<li><p>Root User</p>
</li>
<li><p>sudo Command (Administrative Tasks)</p>
</li>
<li><p>ip addr Command</p>
</li>
<li><p>Install Packages</p>
</li>
<li><p>Remove Packages</p>
</li>
<li><p>Introduction to Nmap</p>
</li>
<li><p>Scan Ports</p>
</li>
<li><p>Wi-Fi Security: System Requirements &amp; Wireless Card</p>
</li>
<li><p>Introduction to Aircrack-ng</p>
</li>
<li><p>Monitor Mode vs Managed Mode</p>
</li>
<li><p>Enable Monitor Mode</p>
</li>
<li><p>Scan Wi-Fi Networks &amp; Capture Traffic</p>
</li>
<li><p>Scan 5GHz Wi-Fi Networks (Theory)</p>
</li>
<li><p>Scan 5GHz Wi-Fi Networks (Practical)</p>
</li>
<li><p>What is a 4-Way Handshake</p>
</li>
<li><p>Capture a 4-Way Handshake</p>
</li>
<li><p>What is a De-authentication Attack</p>
</li>
<li><p>Capture 4-Way Handshake Using De-authentication Attack</p>
</li>
<li><p>Wordlists &amp; Dictionary Attacks</p>
</li>
<li><p>Crack / Recover Wi-Fi Password</p>
</li>
<li><p>Detect De-authentication Attacks /Threats</p>
</li>
<li><p>Wireshark Tutorial</p>
</li>
</ul>
<p>Watch the full course on <a target="_blank" href="https://youtu.be/ug8W0sFiVJo">the freeCodeCamp.org YouTube channel</a> (4-hour watch).</p>
<div class="embed-wrapper">
        <iframe width="560" height="315" src="https://www.youtube.com/embed/ug8W0sFiVJo" style="aspect-ratio: 16 / 9; width: 100%; height: auto;" title="YouTube video player" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share" referrerpolicy="strict-origin-when-cross-origin" allowfullscreen="" loading="lazy"></iframe></div>
 ]]>
                </content:encoded>
            </item>
        
            <item>
                <title>
                    <![CDATA[ React’s Critical "React2Shell" Vulnerability — What You Should Know, and How to Upgrade Your App ]]>
                </title>
                <description>
                    <![CDATA[ Web development is always evolving, and sometimes those changes happen a bit under the hood. One such change involved the shift to React Server Components (RSC). If you’re a NextJS or React developer, especially using the App Router, understanding th... ]]>
                </description>
                <link>https://www.freecodecamp.org/news/reacts-critical-react2shell-vulnerability-what-you-should-know-and-how-to-upgrade-your-app/</link>
                <guid isPermaLink="false">6939b6ee10076c81dd6c3f49</guid>
                
                    <category>
                        <![CDATA[ React2Shell ]]>
                    </category>
                
                    <category>
                        <![CDATA[ React ]]>
                    </category>
                
                    <category>
                        <![CDATA[ Next.js ]]>
                    </category>
                
                    <category>
                        <![CDATA[ hacking ]]>
                    </category>
                
                    <category>
                        <![CDATA[ cybersecurity ]]>
                    </category>
                
                <dc:creator>
                    <![CDATA[ Arunachalam B ]]>
                </dc:creator>
                <pubDate>Wed, 10 Dec 2025 18:07:42 +0000</pubDate>
                <media:content url="https://cdn.hashnode.com/res/hashnode/image/upload/v1765328805925/a9c016a1-90a9-4123-bbb0-17c7d46da035.png" medium="image" />
                <content:encoded>
                    <![CDATA[ <p>Web development is always evolving, and sometimes those changes happen a bit under the hood. One such change involved the shift to React Server Components (RSC). If you’re a NextJS or React developer, especially using the App Router, understanding the new security alert is really important for keeping your apps safe and secure.</p>
<h2 id="heading-table-of-contents">Table of Contents</h2>
<ul>
<li><p><a class="post-section-overview" href="#heading-what-is-react2shell">What is "React2Shell"?</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-why-is-this-happening-now">Why is this Happening Now?</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-should-you-worry-about-this-change">Should You Worry About this Change?</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-is-this-mandatory">Is this Mandatory?</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-how-bad-can-it-get-the-extent-of-exploitation">How Bad Can It Get? The Extent of Exploitation</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-what-would-be-the-code-change-for-this">What Would be the Code Change for This?</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-advanced-verify-with-the-original-exploit-poc">Advanced: Verify with the Original Exploit (PoC)</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-emergency-response-what-if-you-were-already-compromised">Emergency Response: What If You Were Already Compromised?</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-conclusion">Conclusion</a></p>
</li>
</ul>
<h2 id="heading-what-is-react2shell">What is "React2Shell"?</h2>
<p>Think of your server receiving data like a mailroom receiving packages.</p>
<p>Usually, a mailroom checks if a package is safe before opening it. But in vulnerable versions of React and NextJS, the "Flight" protocol (used to communicate between the server and client) acts like a mailroom that blindly opens every package and follows any instructions inside immediately.</p>
<p>This vulnerability (CVE-2025-55182) allows an attacker to send a specifically crafted "package" (HTTP request) that forces your server to execute malicious code – like stealing passwords or installing viruses –without even logging in.</p>
<h2 id="heading-why-is-this-happening-now">Why is this Happening Now?</h2>
<p>It's all about how modern frameworks handle data serialization. There are a few reasons why this was just discovered.</p>
<p>First, React has complex serialization. To make Server Components seamless, React sends complex data structures back and forth.</p>
<p>Second, it has the "Flight" protocol. The vulnerability was found in how this specific protocol de-serializes (unpacks) data. It was too trusting of the input it received from the client side.</p>
<h2 id="heading-should-you-worry-about-this-change">Should You Worry About this Change?</h2>
<p>You need to pay attention if your app qualifies for any of the below:</p>
<ul>
<li><p><strong>You are using NextJS App Router:</strong> This is the default in newer NextJS versions (v13+).</p>
</li>
<li><p><strong>You are using React 19:</strong> Specifically versions with Server Components enabled.</p>
</li>
<li><p><strong>You use Server Actions:</strong> If your app takes user input and processes it on the server using React's server actions.</p>
</li>
</ul>
<h2 id="heading-is-this-mandatory">Is this Mandatory?</h2>
<p><strong>Yes.</strong> This is a critical security update. If your app qualifies in any of the above scenarios, you need to act immediately. Because, this vulnerability is being exploited right now.</p>
<h2 id="heading-how-bad-can-it-get-the-extent-of-exploitation">How Bad Can It Get? The Extent of Exploitation</h2>
<p>You might be thinking, "My site is just a simple content wrapper, surely I'm not a target?" Unfortunately, with Remote Code Execution (RCE), the attacker doesn't just "break" your site – they own the server it runs on.</p>
<p>Here is exactly what a hacker can do once they exploit this vulnerability:</p>
<h3 id="heading-total-environment-theft">Total Environment Theft</h3>
<p>The most immediate risk is your <code>.env</code> file. Attackers can execute code to read your environment variables, instantly gaining access to your AWS Secret Keys, Database passwords, Stripe API keys, and OpenAI tokens.</p>
<h3 id="heading-the-shell-access">The "Shell" Access</h3>
<p>As the name "React2Shell" implies, attackers can open a reverse shell. This gives them a command-line interface on your server, allowing them to browse your file system as if they were sitting in front of your computer.</p>
<h3 id="heading-lateral-movement">Lateral Movement</h3>
<p>Once inside your NodeJS server, they are behind your firewall. They can now attack your internal services (like Redis, internal databases, or private micro-services) that are usually blocked from the outside world.</p>
<h3 id="heading-supply-chain-poisoning">Supply Chain Poisoning</h3>
<p>If your build server is vulnerable, an attacker could potentially inject malicious code into your deployment pipeline, affecting every user who visits your site in the future.</p>
<h3 id="heading-botnet-recruitment">Botnet Recruitment</h3>
<p>Hackers often automate these attacks to install crypto-miners, using your server's CPU (which you pay for!) to mine digital currency for them, often crashing your application in the process.</p>
<h2 id="heading-what-would-be-the-code-change-for-this">What Would be the Code Change for This?</h2>
<p>You don’t need to rewrite your application code, but you must update your dependencies in your release line.</p>
<p>The vulnerability is fully resolved in the following patched NextJS releases:</p>
<ul>
<li><p>15.0.5</p>
</li>
<li><p>15.1.9</p>
</li>
<li><p>15.2.6</p>
</li>
<li><p>15.3.6</p>
</li>
<li><p>15.4.8</p>
</li>
<li><p>15.5.7</p>
</li>
<li><p>16.0.7</p>
</li>
</ul>
<p>Patched canary releases for NextJS 15 and 16:</p>
<ul>
<li><p>15.6.0-canary.58 (for 15.x canary releases)</p>
</li>
<li><p>16.1.0-canary.12 (for 16.x canary releases)</p>
</li>
</ul>
<p>These versions include the hardened React Server Components implementation.</p>
<p>Here are the patched versions for React JS:</p>
<ul>
<li><p>19.0.1</p>
</li>
<li><p>19.1.2</p>
</li>
<li><p>19.2.1</p>
</li>
</ul>
<p>Frameworks and bundlers using the aforementioned packages should install the latest versions provided by their respective maintainers.</p>
<p>Alternatively, you can run <code>npx fix-react2shell-next</code> in your NextJS project to launch an interactive tool which can check versions and perform deterministic version bumps per the recommended versions above. See the <a target="_blank" href="https://github.com/vercel-labs/fix-react2shell-next">GitHub repository</a> for full details.</p>
<p><strong>There is no workaround other than upgrading to a patched version.</strong></p>
<p>It’s highly recommended to rotate all your application secrets, once you have patched your version and re-deployed your application.</p>
<h2 id="heading-advanced-verify-with-the-original-exploit-poc">Advanced: Verify with the Original Exploit (PoC)</h2>
<p>If you want to be 100% sure your patch is working, or if you want to understand how the attack actually works, you can use the original Proof of Concept (PoC) created by the security researcher (Lachlan Davidson) who found the bug.</p>
<p><strong>Repository:</strong> <a target="_blank" href="https://github.com/lachlan2k/React2Shell-CVE-2025-55182-original-poc">React2Shell-CVE-2025-55182-original-poc</a></p>
<p>Lachlan provided three variations of the exploit script. The most important one for testing is <code>01-submitted-poc.js</code>, which is the exact, simplified version submitted to Meta for the bug bounty.</p>
<h3 id="heading-how-the-exploit-works">How the Exploit Works</h3>
<p>According to the repository, the attack works by tricking the parser:</p>
<ol>
<li><p>The attacker sends a payload using <code>$@x</code> to access a specific data <code>Chunk</code>.</p>
</li>
<li><p>They "plant" a <code>.then</code> function on a fake object.</p>
</li>
<li><p>The JavaScript runtime thinks it is handling a Promise and tries to "unravel" it.</p>
</li>
<li><p>This allows the attacker to re-enter the parser with a malicious fake chunk, giving them access to internal server gadgets (like <code>_response</code>) to execute code (RCE).</p>
</li>
</ol>
<h3 id="heading-steps-to-recreate-the-issue">Steps to Recreate the Issue</h3>
<p><strong>⚠️ WARNING:</strong> Only run this against a local development server (<a target="_blank" href="http://localhost"><code>localhost</code></a>) that you own. Never run this against production servers or public websites.</p>
<p><strong>Note:</strong> I forked Lachlan’s repo and made minor changes to make it easy for you to run the script.</p>
<h4 id="heading-step-1-clone-the-repository">Step 1: Clone the Repository</h4>
<p>Run the following commands to clone the repository, navigate into the project, and install dependencies:</p>
<pre><code class="lang-bash">git <span class="hljs-built_in">clone</span> https://github.com/arunachalam-b/React2Shell-CVE-2025-55182-original-poc.git
<span class="hljs-built_in">cd</span> React2Shell-CVE-2025-55182-original-poc
npm i
</code></pre>
<h4 id="heading-step-2-run-a-vulnerable-local-server">Step 2: Run a Vulnerable Local Server</h4>
<p>Start your NextJS application locally (ensure it’s running a vulnerable version, for example NextJS 15.0.0, for the test to succeed).</p>
<pre><code class="lang-bash">npm run dev
<span class="hljs-comment"># usually runs on http://localhost:3000</span>
</code></pre>
<h4 id="heading-step-3-execute-the-test">Step 3: Execute the Test</h4>
<p>You will need to modify the script or use a tool like <code>curl</code> to send the payload structure found in <code>01-submitted-poc.js</code> to your server's endpoint (usually a Server Action endpoint). Or simply run the following command if your app is accessible at <code>http://localhost:3000</code>:</p>
<pre><code class="lang-bash">node 01-submitted-poc.js
</code></pre>
<p>If the exploit succeeds (on the vulnerable version), the console will log the execution of the code (RCE). If the exploit fails (after you patch), the server will either reject the request or error out safely.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1765298324009/7a8158bb-30cc-4604-9591-4503a4c8d655.png" alt="This response on running the script indicates your server is vulnerable" class="image--center mx-auto" width="1160" height="461" loading="lazy"></p>
<p>You can also confirm if your infected web server prints <code>50</code> in the console. Because we inject the code to do a calculation (look at <code>_prefix</code> field in the below JSON) that results in <code>50</code>.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1765298178869/417d5f3c-15d2-4806-854c-f4216d336bd9.png" alt="The payload used to demonstrate this hack" class="image--center mx-auto" width="568" height="367" loading="lazy"></p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1765298285565/38bcbcfa-390f-4198-9db1-218d2ed0dd65.png" alt="The 50 in your NextJS console indicates the hackers code has been executed on your server" class="image--center mx-auto" width="707" height="208" loading="lazy"></p>
<p>After you apply the fix, you should see an error while running the script. In this case, as I’m using NextJS v15.1, the fix is upgrading the <code>next</code> package to version <code>15.1.9</code>. Here are the screenshots after upgrading the package and running the script.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1765298558607/36103fd0-2a23-44f5-bff6-f979255a9765.png" alt="Response on running script after applying the fix" class="image--center mx-auto" width="1160" height="572" loading="lazy"></p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1765298580698/53bcd836-013a-4291-98df-2d9188512a2c.png" alt="The console does not print 50 while running same script which indicates the hackers code is not executed on your server after applying the fix" class="image--center mx-auto" width="1321" height="251" loading="lazy"></p>
<h4 id="heading-step-4-verification">Step 4: Verification</h4>
<p>Once you have confirmed the exploit works on the old version, update your packages (as shown in the section above) and run the script again. It should no longer trigger the code execution.</p>
<h2 id="heading-emergency-response-what-if-you-were-already-compromised">Emergency Response: What If You Were Already Compromised?</h2>
<p>If you suspect your server was exposed to the internet with a vulnerable version, assume the worst. A hacker may have already stolen your keys or left a "backdoor" to return later. Patching the code alone is NOT enough in this case.</p>
<p>Follow this <strong>"Nuke and Pave"</strong> protocol immediately:</p>
<h3 id="heading-step-1-isolate-and-shutdown">Step 1: Isolate and Shutdown</h3>
<p>Take the compromised server offline immediately. Do not try to "fix" it while it is running.</p>
<h3 id="heading-step-2-rotate-all-secrets-crucial-step">Step 2: Rotate ALL Secrets (Crucial Step)</h3>
<p>Assume every secret in your <code>.env</code> file is in the hands of a hacker. You must generate new ones:</p>
<ul>
<li><p>Change the password for your database users.</p>
</li>
<li><p>Rotate AWS Access Keys, Google Cloud Service Account keys, and so on.</p>
</li>
<li><p>Roll your Stripe/PayPal/Razorpay API keys.</p>
</li>
<li><p>Rotate your <code>NEXTAUTH_SECRET</code> or any JWT signing keys.</p>
</li>
</ul>
<h3 id="heading-step-3-do-not-clean-rebuild">Step 3: Do Not "Clean" — Rebuild</h3>
<p>Do not attempt to find and delete malware files on the server. Hackers are good at hiding.</p>
<ul>
<li><p>Destroy the existing container, droplet, or EC2 instance entirely.</p>
</li>
<li><p>Build a fresh instance from your source code (after applying the patch).</p>
</li>
</ul>
<h3 id="heading-step-4-audit-your-logs">Step 4: Audit Your Logs</h3>
<p>Look at your database and cloud provider logs. Did anyone download your entire user database? Did anyone spin up expensive GPU instances on your AWS account? Check for unusual activity that occurred before you patched.</p>
<h2 id="heading-conclusion">Conclusion</h2>
<p>In this article, you learned about the "React2Shell" vulnerability, how to verify it using the original developer's tools, and how to upgrade your app to secure your Server Components. I hope you have a clear idea about why this update is urgent. By being proactive now, you can avoid a catastrophic data breach.</p>
<p>You can follow my <a target="_blank" href="https://x.com/AI_Techie_Arun">Twitter/X account</a> to receive the top AI news everyday. If you wish to learn more about cybersecurity, <a target="_blank" href="https://5minslearn.gogosoon.com/?ref=react2shell-vulnerability">subscribe to my email newsletter</a> and follow me on social media.</p>
 ]]>
                </content:encoded>
            </item>
        
            <item>
                <title>
                    <![CDATA[ VPNs vs Proxies: What are the Differences? ]]>
                </title>
                <description>
                    <![CDATA[ In the age of online privacy, two tools are often mentioned together: VPNs and proxies. Both hide your IP address and help you browse the internet more privately, but they work in different ways and serve different purposes. From simple security to w... ]]>
                </description>
                <link>https://www.freecodecamp.org/news/vpns-vs-proxies-what-are-the-differences/</link>
                <guid isPermaLink="false">690e629981a4964f4b12b422</guid>
                
                    <category>
                        <![CDATA[ cybersecurity ]]>
                    </category>
                
                    <category>
                        <![CDATA[ vpn ]]>
                    </category>
                
                <dc:creator>
                    <![CDATA[ Manish Shivanandhan ]]>
                </dc:creator>
                <pubDate>Fri, 07 Nov 2025 21:20:25 +0000</pubDate>
                <media:content url="https://cdn.hashnode.com/res/hashnode/image/upload/v1762549605269/165d4e53-a54e-46b7-95b6-f2b76bcdfc53.png" medium="image" />
                <content:encoded>
                    <![CDATA[ <p>In the age of online privacy, two tools are often mentioned together: VPNs and proxies.</p>
<p>Both hide your IP address and help you browse the internet more privately, but they work in different ways and serve different purposes. From simple security to web scraping for LLM training, both serve various purposes for businesses.</p>
<p>If you have ever wondered which one you should use, this article will help you understand how they work, their main differences, and where residential proxies fit into the picture.</p>
<h2 id="heading-what-well-cover">What We’ll Cover</h2>
<ul>
<li><p><a class="post-section-overview" href="#heading-what-is-a-vpn">What is a VPN?</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-what-is-a-proxy">What is a Proxy?</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-the-core-difference-between-vpns-and-proxies">The Core Difference Between VPNs and Proxies</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-performance-and-speed">Performance and Speed</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-use-cases-for-vpns">Use Cases for VPNs</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-use-cases-for-proxies">Use Cases for Proxies</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-how-to-combine-vpns-and-residential-proxies">How to Combine VPNs and Residential Proxies</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-which-one-should-you-choose">Which One Should You Choose?</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-the-future-of-privacy-tools">The Future of Privacy Tools</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-conclusion">Conclusion</a></p>
</li>
</ul>
<h2 id="heading-what-is-a-vpn">What is a VPN?</h2>
<p>A <a target="_blank" href="https://en.wikipedia.org/wiki/Virtual_private_network">Virtual Private Network</a>, or VPN, is a service that creates a secure and encrypted tunnel between your device and the internet.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1762778687218/416de0f8-f05e-4e2f-a52a-e6f029594dcc.png" alt="VPN Architecture" class="image--center mx-auto" width="855" height="318" loading="lazy"></p>
<p>When you connect to a VPN, all your traffic passes through a remote server operated by the VPN provider. This hides your real IP address and encrypts everything you send or receive.</p>
<p>VPNs are often used by individuals who want to protect their privacy or access content that is restricted in their region.</p>
<p>For example, someone in India can use a VPN to connect to a U.S. server and access websites that are available only in the United States. Because the connection is encrypted, internet service providers and hackers cannot see which websites you visit or what data you exchange.</p>
<h2 id="heading-what-is-a-proxy">What is a Proxy?</h2>
<p>A <a target="_blank" href="https://en.wikipedia.org/wiki/Proxy_server">proxy</a> acts as a middleman between your device and the internet.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1762778705089/7b52dbba-a479-428e-843b-caea962386e8.png" alt="Proxy Architecture" class="image--center mx-auto" width="1000" height="563" loading="lazy"></p>
<p>When you connect to a proxy, your request is first sent to the proxy server, which then forwards it to the target website. The website sees the proxy’s IP address instead of your own.</p>
<p>Unlike VPNs, proxies usually do not encrypt your traffic. This means that while your IP address is hidden, the data itself can still be visible to others.</p>
<p>Proxies are often used for tasks like web scraping, managing multiple social media accounts, or accessing geo-restricted sites in a lightweight way.</p>
<p>There are different types of proxies, such as datacenter proxies, mobile proxies, and residential proxies. Among these, <a target="_blank" href="https://netnut.io/datacenter-vs-residential-proxy-the-ultimate-guide">residential proxies</a> are the most trusted because they use real IP addresses assigned by internet service providers.</p>
<h2 id="heading-the-core-difference-between-vpns-and-proxies">The Core Difference Between VPNs and Proxies</h2>
<p>The biggest difference between a VPN and a proxy lies in encryption.</p>
<p>VPNs encrypt all network traffic from your device, while most proxies do not. This means VPNs provide a higher level of security and privacy. Even if someone intercepts your data, they cannot read it.</p>
<p>Proxies, on the other hand, focus more on IP masking rather than full encryption. They are lighter, faster, and more flexible for specific use cases like automation, scraping, or content testing.</p>
<p>For example, a company that needs to collect product data from multiple e-commerce sites will use residential proxies rather than a VPN because proxies allow scalable, distributed access from different IP addresses.</p>
<p>Another key difference is the level of system-wide protection. A VPN <a target="_blank" href="https://cloud.google.com/learn/what-is-encryption">encrypts all the traffic</a> coming from your device, including background apps.</p>
<p>A proxy typically only routes traffic from specific browsers or applications. This makes VPNs better for personal privacy and proxies better for targeted tasks.</p>
<h2 id="heading-performance-and-speed">Performance and Speed</h2>
<p>Since VPNs encrypt traffic, they can reduce speed due to the extra processing involved. Proxies, by contrast, are often faster because they skip encryption and route only specific requests.</p>
<p>However, not all proxies are equal. Datacenter proxies may be fast but easier to detect, while residential proxies are slower but far more reliable for tasks that require realism. Businesses often accept this small trade-off in speed for better accuracy and reduced blocking.</p>
<p>VPNs usually have fewer IPs and servers compared to proxy networks, which can limit their flexibility. Proxies can rotate thousands of IPs automatically, which helps avoid bans and distribute requests efficiently.</p>
<h2 id="heading-use-cases-for-vpns">Use Cases for VPNs</h2>
<p>VPNs are ideal for individuals who value security and privacy. They are useful for browsing safely on public Wi-Fi, accessing restricted websites, or hiding browsing habits from internet service providers.</p>
<p>Remote workers often use VPNs to securely access corporate networks. Journalists and activists rely on them to bypass censorship or protect communication in restrictive regions.</p>
<p>For everyday users, a VPN provides a simple and effective way to browse anonymously and encrypt all data traffic.</p>
<h2 id="heading-use-cases-for-proxies">Use Cases for Proxies</h2>
<p><a target="_blank" href="https://www.freecodecamp.org/news/what-is-a-proxy-server-in-english-please/">Proxies</a> shine in automation and business scenarios. They are essential for data gathering, web scraping, and digital marketing. By using residential proxies, companies can collect information from multiple websites without getting blocked.</p>
<p>For example, a brand can track how its ads appear to users in different countries. E-commerce businesses can compare competitor prices or monitor product listings in real time. Social media managers use proxies to handle multiple accounts without triggering platform restrictions.</p>
<p>Proxies also help in large-scale <a target="_blank" href="https://netnut.io/llm-web-scraping-guide/">web scraping for LLM training</a>. They allow businesses to gather public data anonymously and at scale without getting blocked or throttled by websites.</p>
<h2 id="heading-how-to-combine-vpns-and-residential-proxies">How to Combine VPNs and Residential Proxies</h2>
<p>In some cases, professionals use both. For example, a researcher may connect to a VPN for encryption and then route specific scraping tasks through residential proxies for location diversity. This hybrid setup balances privacy and data collection efficiency.</p>
<p>Combining them also reduces the risk of IP bans. If a target site starts blocking one set of IPs, the user can switch networks seamlessly. This approach is popular in cybersecurity testing, ad verification, and large-scale monitoring.</p>
<h2 id="heading-which-one-should-you-choose">Which One Should You Choose?</h2>
<p>If your goal is privacy, use a VPN. It secures your entire connection and hides all your online activities. If your goal is automation, data collection, or region-specific testing, use proxies.</p>
<p>Residential proxies are especially effective when websites have strong anti-bot protection or region-based restrictions. They combine anonymity with authenticity, making your traffic look like that of a regular home user.</p>
<p>For individuals who need both security and flexibility, a mix of VPN and proxy can work best. You can encrypt your connection with a VPN and use residential proxies for specific tools or scripts that need rotation and scale.</p>
<h2 id="heading-the-future-of-privacy-tools">The Future of Privacy Tools</h2>
<p>As online tracking becomes more advanced, tools like VPNs and residential proxies are becoming essential for both individuals and businesses. Companies use them to access unbiased market data and protect digital assets, while individuals use them to browse safely and privately.</p>
<p>In the future, we may see hybrid solutions that blend the privacy of VPNs with the scalability of proxy networks. These systems could automatically switch between encryption and proxy routing based on the task at hand, providing a seamless balance between speed and security.</p>
<h2 id="heading-conclusion">Conclusion</h2>
<p>VPNs and proxies both protect your identity online, but they serve different purposes. VPNs focus on privacy and encryption, while proxies , especially residential proxies , focus on scalability and access.</p>
<p>Understanding how each works helps you choose the right tool for your needs. Whether you want to stay anonymous, collect data safely, or test websites from different countries, using the right combination of VPN and residential proxies can give you both privacy and power in the digital world.</p>
<p><em>Hope you enjoyed this article. Find me on</em> <a target="_blank" href="https://linkedin.com/in/manishmshiva"><em>Linkedin</em></a> <em>or</em> <a target="_blank" href="https://manishshivanandhan.com/"><em>visit my website</em></a><em>.</em></p>
 ]]>
                </content:encoded>
            </item>
        
            <item>
                <title>
                    <![CDATA[ Top Cybersecurity Certifications to Boost Your Career ]]>
                </title>
                <description>
                    <![CDATA[ Cybersecurity is one of the fastest-growing fields in the world today.  Every year, more companies face threats that put their data, reputation, and customer trust at risk. As these threats grow, so does the demand for skilled cybersecurity professio... ]]>
                </description>
                <link>https://www.freecodecamp.org/news/top-cybersecurity-certifications-to-boost-your-career/</link>
                <guid isPermaLink="false">690933925fbdca38da0ea33b</guid>
                
                    <category>
                        <![CDATA[ cybersecurity ]]>
                    </category>
                
                    <category>
                        <![CDATA[ Security ]]>
                    </category>
                
                    <category>
                        <![CDATA[ Certification ]]>
                    </category>
                
                <dc:creator>
                    <![CDATA[ Manish Shivanandhan ]]>
                </dc:creator>
                <pubDate>Mon, 03 Nov 2025 22:58:26 +0000</pubDate>
                <media:content url="https://cdn.hashnode.com/res/hashnode/image/upload/v1762183746663/9f26b839-70c5-4ab6-b5ff-53ead8d2311b.png" medium="image" />
                <content:encoded>
                    <![CDATA[ <p>Cybersecurity is one of the fastest-growing fields in the world today. </p>
<p>Every year, more companies face threats that put their data, reputation, and customer trust at risk. As these threats grow, so does the demand for skilled cybersecurity professionals. </p>
<p>One of the best ways to stand out in this competitive field is to earn a recognised certification. Certifications not only validate your skills but also open doors to better job opportunities, higher pay, and global recognition.</p>
<p>In this article, we will look at the top five cybersecurity certifications that can help you build or advance your career. Whether you are new to cybersecurity or already working in the field, these certifications can take your skills and credibility to the next level.</p>
<h2 id="heading-what-we-cover"><strong>What we Cover</strong></h2>
<ul>
<li><p><a class="post-section-overview" href="#heading-certified-information-systems-security-professional-cissp">Certified Information Systems Security Professional (CISSP)</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-certified-information-security-manager-cism">Certified Information Security Manager (CISM)</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-certified-ethical-hacker-ceh">Certified Ethical Hacker (CEH)</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-comptia-security">CompTIA Security+</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-offensive-security-certified-professional-oscp">Offensive Security Certified Professional (OSCP)</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-choosing-the-right-certification">Choosing the Right Certification</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-conclusion">Conclusion</a></p>
</li>
</ul>
<h2 id="heading-certified-information-systems-security-professional-cissp"><strong>Certified Information Systems Security Professional (CISSP)</strong></h2>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1761892831028/e7c568fc-a0de-403f-bd51-82ec50926e93.jpeg" alt="CISSP" class="image--center mx-auto" width="1024" height="512" loading="lazy"></p>
<p>The <a target="_blank" href="https://www.isc2.org/certifications/cissp">CISSP, offered by (ISC)²</a>, is considered one of the most prestigious certifications in cybersecurity. It’s designed for experienced professionals who want to prove their ability to design, implement, and manage an enterprise-level security program.</p>
<p>CISSP is often seen as a leadership certification rather than a purely technical one. It demonstrates a deep understanding of security principles across multiple domains, including areas such as security and risk management, asset security, security architecture and engineering, communication and network security, identity and access management, security assessment and testing, security operations, and software development security.</p>
<p>Together, these eight domains cover every major aspect of protecting an organization’s information systems. Several security agencies have highlighted the <a target="_blank" href="https://www.1kosmos.com/security-glossary/cissp/">importance of CISSP certification</a>, so it’s an expected certification if you are looking for senior-level security roles.</p>
<p>To earn the CISSP, you need at least five years of professional experience in two or more of these domains. The exam itself is challenging and is designed to test not just what you know, but how you apply that knowledge in complex, real-world scenarios. It requires you to think both technically and strategically, balancing hands-on expertise with business and management considerations.</p>
<p>Even if you don’t yet meet the experience requirement, you can still pass the exam and become an Associate of (ISC)². Once you complete the necessary work experience, you can upgrade to a full CISSP.</p>
<p>CISSP holders are often found in senior roles such as Chief Information Security Officer, Security Architect, or IT Director. Employers worldwide value this certification because it demonstrates your understanding of both the technical and organisational aspects of cybersecurity.</p>
<p>If your goal is to move into senior management or to lead enterprise-wide security programs, CISSP remains one of the most respected and valuable certifications you can pursue.</p>
<h2 id="heading-certified-information-security-manager-cism"><strong>Certified Information Security Manager (CISM)</strong></h2>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1761892925031/521f3e9d-07b6-40f1-b6ef-9d00ace8bc3f.jpeg" alt="CISM" class="image--center mx-auto" width="1200" height="480" loading="lazy"></p>
<p>The <a target="_blank" href="https://www.isaca.org/credentialing/cism">CISM, managed by ISACA</a>, is a globally recognized certification for professionals who manage, design, and oversee an enterprise’s information security program. It’s particularly valuable for those who want to move from technical roles into management.</p>
<p>CISM focuses on four main areas: information security governance, risk management, program development and management, and incident management. These domains reflect the skills required to align an organization’s security strategy with its overall business goals.</p>
<p>Unlike certifications that focus mainly on technical details, CISM emphasizes the connection between business needs and security policies. It prepares you to communicate effectively with senior management and to justify security investments in terms of business outcomes.</p>
<p>Industry leaders like DestCert have highlighted the <a target="_blank" href="https://destcert.com/resources/cism-cost/">importance of the CISM certification</a> for its role in shaping professionals who understand both the technical and strategic sides of cybersecurity. As organizations increasingly seek leaders who can balance risk, compliance, and technology, the demand for CISM-certified experts continues to grow.</p>
<p>To earn the CISM, you need at least five years of experience in information security management, with at least three years of experience in at least three of the four CISM domains. After passing the exam, you also need to agree to ISACA’s code of ethics and commit to continuing education to keep your certification active.</p>
<h2 id="heading-certified-ethical-hacker-ceh"><strong>Certified Ethical Hacker (CEH)</strong></h2>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1761892973456/f8c760ae-734e-4420-8961-d41a931458cc.jpeg" alt="CEH" class="image--center mx-auto" width="1280" height="720" loading="lazy"></p>
<p>The <a target="_blank" href="https://www.eccouncil.org/train-certify/certified-ethical-hacker-ceh/">CEH certification</a>, offered by EC-Council, is one of the most recognized credentials for those who want to specialize in ethical hacking and penetration testing. It teaches you how to think like a hacker, identify vulnerabilities, and secure systems before attackers can exploit them.</p>
<p>CEH covers topics such as footprinting, scanning, enumeration, system hacking, web application hacking, and social engineering. The goal is to understand the attacker’s mindset so you can defend more effectively. It also includes hands-on labs that allow you to practice hacking techniques in a controlled environment.</p>
<p>This certification is often the first step for many cybersecurity professionals who want to move into roles such as penetration tester, red team member, or security analyst. It demonstrates that you know how to identify weaknesses and apply countermeasures to protect systems and data. Since it’s a beginner certification, there are often questions whether <a target="_blank" href="https://www.eccouncil.org/cybersecurity-exchange/ethical-hacking/is-ceh-worth-it/">CEH is worth taking</a>. But it is still the most widely known cybersecurity certification in existence.</p>
<p>Employers value CEH because it bridges the gap between theory and practice. While it’s not as advanced as some other certifications, it provides a solid foundation for anyone interested in offensive security. If you are looking to start a career in ethical hacking or penetration testing, CEH is an excellent choice.</p>
<h2 id="heading-comptia-security"><strong>CompTIA Security+</strong></h2>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1761893006747/1a81f293-f6d5-4292-a23f-d4e185df03d0.png" alt="Security+" class="image--center mx-auto" width="1200" height="666" loading="lazy"></p>
<p><a target="_blank" href="https://www.comptia.org/en/certifications/security/">CompTIA Security+</a> is one of the most widely accepted entry-level certifications in cybersecurity. It’s ideal for beginners who want to establish a strong foundation in security concepts before moving on to advanced certifications.</p>
<p>Security+ covers essential topics such as threat management, cryptography, risk management, identity management, and incident response. It also focuses on practical skills needed to perform day-to-day security tasks. Unlike some certifications that require extensive experience, Security+ is accessible to anyone with a basic understanding of networking and IT systems.</p>
<p>The certification is vendor-neutral, which means it applies to a broad range of technologies rather than being tied to a specific company or product. Many organizations use it as a benchmark for hiring junior security professionals or system administrators.</p>
<p>Security+ is also approved by the U.S. Department of Defense for its cybersecurity workforce, which further adds to its credibility. <a target="_blank" href="https://www.comptia.org/en/blog/how-do-i-get-my-comptia-security-certification/">Passing the exam</a> shows that you understand the key principles of cybersecurity and are ready to take on entry-level security roles.</p>
<p>After earning Security+, many professionals go on to pursue more advanced certifications such as CISSP or CISM. It serves as a perfect starting point for building a long-term cybersecurity career.</p>
<h2 id="heading-offensive-security-certified-professional-oscp"><strong>Offensive Security Certified Professional (OSCP)</strong></h2>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1761893051728/04ea83a1-f979-4c7e-a69d-eb18f665d6e8.png" alt="OSCP" class="image--center mx-auto" width="1200" height="630" loading="lazy"></p>
<p>The <a target="_blank" href="https://en.wikipedia.org/wiki/Offensive_Security_Certified_Professional">Offensive Security Certified Professional (OSCP)</a>, offered by Offensive Security, is one of the most respected certifications in the field of ethical hacking and penetration testing. Known for its hands-on and highly practical nature, the OSCP sets a high standard for proving real-world hacking skills.</p>
<p>Unlike many certifications that focus on theory or multiple-choice questions, the OSCP exam challenges you to demonstrate your ability to find and exploit vulnerabilities in live machines within a controlled environment. It requires you to hack into multiple systems, gain administrative access, and document your process in a professional penetration testing report.</p>
<p>The OSCP exam is widely regarded as one of the toughest in cybersecurity. Candidates are given 24 hours to complete the test, during which they must identify, exploit, and escalate privileges across different targets. This format tests not only your technical knowledge but also your persistence, creativity, and problem-solving skills.</p>
<p><a target="_blank" href="https://astikrawat.medium.com/oscp-step-by-step-guide-to-success-9ff3d189dbb2">Earning the OSCP</a> shows that you can think like an attacker, apply advanced exploitation techniques, and work methodically under pressure. It’s ideal for people who want to advance their careers in offensive security, red teaming, or penetration testing. Common job roles for OSCP holders include penetration tester, security consultant, red team operator, and exploit developer.</p>
<p>The OSCP focuses on practical skills such as network scanning, enumeration, buffer overflows, privilege escalation, and pivoting through networks. The accompanying course, “Penetration Testing with Kali Linux (PWK),” provides comprehensive training that helps candidates prepare for the exam by learning through real-world scenarios.</p>
<p>Employers value the OSCP because it demonstrates more than just knowledge. It also proves your ability to apply that knowledge in complex, hands-on situations. Many in the cybersecurity industry consider it a rite of passage for ethical hackers and a mark of true technical skill.</p>
<p>If your goal is to become an advanced penetration tester or to build deep technical expertise in offensive security, earning the OSCP is one of the most rewarding certifications you can pursue.</p>
<h2 id="heading-choosing-the-right-certification"><strong>Choosing the Right Certification</strong></h2>
<p>Selecting the right certification depends on your current experience and career goals. If you are new to cybersecurity, CompTIA Security+ is a great place to start. If you already have some experience and want to specialize in ethical hacking, the CEH will help you build practical offensive skills.</p>
<p>For those aiming for leadership and management positions, CISM and CISSP are both excellent choices. CISM focuses on aligning security with business strategy, while CISSP focuses on designing and managing enterprise-wide security programs. If your career involves core technical expertise, the OSCP is a natural next step.</p>
<p>Each certification not only validates your technical skills but also strengthens your reputation as a trusted cybersecurity professional. Employers often use certifications as a signal of commitment and capability, making them a valuable investment in your career.</p>
<h2 id="heading-conclusion"><strong>Conclusion</strong></h2>
<p>Cybersecurity is a field that never stops evolving. New threats emerge every day, and the technology used to defend against them changes just as quickly. To stay relevant and competitive, you’ll need to keep learning and proving your expertise. This will help you both advance your career and be prepared to defend what matters most.</p>
<p><em>Hope you enjoyed this article. Find me on</em> <a target="_blank" href="https://linkedin.com/in/manishmshiva"><em>Linkedin</em></a> <em>or</em> <a target="_blank" href="https://manishshivanandhan.com/"><strong><em>visit my website</em></strong></a></p>
 ]]>
                </content:encoded>
            </item>
        
            <item>
                <title>
                    <![CDATA[ Learn Cybersecurity from Harvard University ]]>
                </title>
                <description>
                    <![CDATA[ freeCodeCamp just posted a Harvard University course that will give you a solid introduction to cybersecurity. It's taught by one of the world's most-loved computer science teachers, Dr. David J. Malan. In this course, you’ll learn how to secure your... ]]>
                </description>
                <link>https://www.freecodecamp.org/news/learn-cybersecurity-from-harvard-university/</link>
                <guid isPermaLink="false">69039b943b42750686df8138</guid>
                
                    <category>
                        <![CDATA[ cybersecurity ]]>
                    </category>
                
                    <category>
                        <![CDATA[ youtube ]]>
                    </category>
                
                <dc:creator>
                    <![CDATA[ Beau Carnes ]]>
                </dc:creator>
                <pubDate>Thu, 30 Oct 2025 17:08:36 +0000</pubDate>
                <media:content url="https://cdn.hashnode.com/res/hashnode/image/upload/v1761844053497/6487b459-add3-4f7a-a50a-258f9a7b771e.jpeg" medium="image" />
                <content:encoded>
                    <![CDATA[ <p>freeCodeCamp just posted a Harvard University course that will give you a solid introduction to cybersecurity.</p>
<p>It's taught by one of the world's most-loved computer science teachers, Dr. David J. Malan.</p>
<p>In this course, you’ll learn how to secure your accounts, data, systems, and software against today’s threats. You’ll also learn how to recognize and evaluate tomorrow’s, both at home and at work. Then you’ll learn how to preserve your own privacy as well.</p>
<p>This course teaches you how to view cybersecurity not in absolute terms but in relative ones as well: as a function of risks and rewards (for an adversary) and costs and benefits (for you).</p>
<p>Dr. Malan presents both high-level and low-level examples of threats, providing students with all they need to know technically to understand both.</p>
<p>Here are the sections in this course:</p>
<ul>
<li><p>Introduction</p>
</li>
<li><p>Securing Accounts</p>
</li>
<li><p>Securing Data</p>
</li>
<li><p>Securing Systems</p>
</li>
<li><p>Securing Software</p>
</li>
<li><p>Preserving Privacy</p>
</li>
</ul>
<p>Watch the course on <a target="_blank" href="https://youtu.be/9HOpanT0GRs">the freeCodeCamp.org YouTube channel</a> (8-hour watch).</p>
<div class="embed-wrapper">
        <iframe width="560" height="315" src="https://www.youtube.com/embed/9HOpanT0GRs" style="aspect-ratio: 16 / 9; width: 100%; height: auto;" title="YouTube video player" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share" referrerpolicy="strict-origin-when-cross-origin" allowfullscreen="" loading="lazy"></iframe></div>
 ]]>
                </content:encoded>
            </item>
        
            <item>
                <title>
                    <![CDATA[ How to Build a Rate Limiter with Redis and Python to Scale Your Apps ]]>
                </title>
                <description>
                    <![CDATA[ If you've ever built a web application, you know that without a proper mechanism to control traffic, your application can become overwhelmed, leading to slow response times, server crashes, and a poor user experience. Even worse, it can leave you vul... ]]>
                </description>
                <link>https://www.freecodecamp.org/news/build-a-rate-limiter-with-redis-and-python/</link>
                <guid isPermaLink="false">68dfe6e0dcc5f825f4d48c85</guid>
                
                    <category>
                        <![CDATA[ Python ]]>
                    </category>
                
                    <category>
                        <![CDATA[ cybersecurity ]]>
                    </category>
                
                    <category>
                        <![CDATA[ api ]]>
                    </category>
                
                <dc:creator>
                    <![CDATA[ Sravan Karuturi ]]>
                </dc:creator>
                <pubDate>Fri, 03 Oct 2025 15:08:16 +0000</pubDate>
                <media:content url="https://cdn.hashnode.com/res/hashnode/image/upload/v1759503803144/4d974610-95dc-4db8-989a-0d705dc4d431.png" medium="image" />
                <content:encoded>
                    <![CDATA[ <p>If you've ever built a web application, you know that without a proper mechanism to control traffic, your application can become overwhelmed, leading to slow response times, server crashes, and a poor user experience. Even worse, it can leave you vulnerable to Denial-of-Service (DoS) attacks. This is where rate limiting comes in.</p>
<p>In this tutorial, you’ll build a distributed rate limiter. This is the kind of system you need when your application is deployed across multiple servers or virtual machines, and you need to enforce a global limit on all incoming requests.</p>
<p>You’ll build a simple URL shortener application and then implement a robust rate limiter for it using a powerful and efficient combination of tools:</p>
<ul>
<li><p>Python and Flask for your web application.</p>
</li>
<li><p>Redis as your high-speed, centralized data store for tracking requests.</p>
</li>
<li><p>Terraform and Proxmox to define and provision your virtual machine infrastructure.</p>
</li>
<li><p>Docker to containerize your application for easy deployment.</p>
</li>
<li><p>Nginx as a load balancer to distribute traffic across your app servers.</p>
</li>
<li><p>k6 to load-test your system and prove that your rate limiter actually works.</p>
</li>
</ul>
<p>This is intended for new developers learning about various system design concepts or for experts who just want a refresher.</p>
<p>By the end of this guide, you'll understand not just the code, but the complete system architecture required to deploy a scalable, resilient application.</p>
<p>Let's get started!</p>
<h2 id="heading-prerequisites">Prerequisites</h2>
<p>While not absolutely required to follow along, I’d recommend setting up a Proxmox server on an old laptop to implement the topics you learn and code along with the article. I recommend this <a target="_blank" href="https://www.youtube.com/watch?v=5j0Zb6x_hOk&amp;list=PLT98CRl2KxKHnlbYhtABg6cF50bYa8Ulo">YouTube playlist</a> for getting started. Please note that I am in no way affiliated with this channel. I just found it helpful for me.</p>
<p>However, If you do not have a local Proxmox server, you can skip that part and just follow along to understand how a rate limiter is built and how it is set up to properly work with multiple servers.</p>
<h2 id="heading-table-of-contents">Table of Contents</h2>
<ul>
<li><p><a class="post-section-overview" href="#heading-prerequisites">Prerequisites</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-the-big-picture-our-system-architecture">The Big Picture: Our System Architecture</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-step-1-how-to-define-the-infrastructure-with-terraform">Step 1: How to Define the Infrastructure with Terraform</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-step-2-how-to-implement-the-rate-limiter-logic-in-python">Step 2: How to Implement the Rate Limiter Logic in Python</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-step-3-containerizing-and-testing">Step 3: Containerizing and Testing</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-conclusion">Conclusion</a></p>
</li>
</ul>
<h2 id="heading-the-big-picture-our-system-architecture">The Big Picture: Our System Architecture</h2>
<p>Before we dive into the code, let's look at the architecture we're building. I will be using <a target="_blank" href="https://www.proxmox.com/en/products/proxmox-virtual-environment/overview">Proxmox Virtual Environment</a> to setup a server cluster just like you would have in a datacenter.</p>
<h3 id="heading-how-to-set-up-proxmox">How to Set Up Proxmox</h3>
<p><code>Proxmox Virtual Environment</code> is an open source platform for virtualization. It lets you manage multiple VMs, ccontainers and other clusters with ease. For instance, I turned my old gaming computer into a Proxmox server which lets me run more than 20 virtual machines on it at the same time, making it similar to my very own datacenter. This lets me experiment with distributed applications by simulating datacenter environments.</p>
<p>To setup your own cluster, all you need is an old computer. You can download the ISO image from <a target="_blank" href="https://www.proxmox.com/en/downloads">here</a> and boot from the USB drive. Once you install it, you can configure the host machine via a web browser on any other computer on the same network.</p>
<p>For example, my proxmox server is located at <code>10.0.0.108</code> and I can access it via the browser on my laptop.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1759194790299/35e9363f-b739-4085-a589-c1bafbac0504.png" alt="Example Proxmox cluster" class="image--center mx-auto" width="3680" height="2206" loading="lazy"></p>
<p>We define all our virtual machines in our <code>main.tf</code> file. And run a simple command <code>terraform apply</code> to spin these servers up. For more reading on how to use Terraform with Proxmox, I recommend this <a target="_blank" href="https://spacelift.io/blog/terraform-proxmox-provider">blog post</a></p>
<p>Back to our use case, we’ll have a few virtual machines that will serve as different kinds of servers:</p>
<ol>
<li><p>A Load balancer</p>
</li>
<li><p>A Rate Limiter ( A Redis Cache )</p>
</li>
<li><p>Two Web Servers</p>
</li>
<li><p>A Postgres database</p>
</li>
<li><p>One Virtual Machine that will test the load by simulating hundreds of calls per minute.</p>
</li>
</ol>
<p>If all of this seems daunting, don’t worry too much about it. You don’t need to set all this up to follow along.</p>
<h3 id="heading-centralized-rate-limiter">Centralized Rate Limiter</h3>
<p>Since our application will run on multiple servers (or "nodes"), we can't store request counts in memory on each individual server. Why? Because each server would have its own separate count, and we wouldn't have a <em>global</em> rate limit.</p>
<p>The solution is to use a centralized data store that all our application nodes can access. This is where Redis comes in.</p>
<p>Here’s a diagram of our setup:</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1758476002871/1d70ce5b-e19c-4d7d-9c0b-cc18840a07bf.png" alt="A Small diagram depicting the architecture we'll form with all these virtual nodes" class="image--center mx-auto" width="2904" height="1173" loading="lazy"></p>
<ol>
<li><p>User requests first hit our Nginx load balancer.</p>
</li>
<li><p>The load balancer distributes the traffic evenly between our two web server VMs. The configuration is simple, using an upstream block to define the servers.</p>
</li>
<li><p>Each web server runs our Python Flask application inside a Docker container.</p>
</li>
<li><p>Before processing any request, the Flask app communicates with the central Redis rate limiter VM to check if the user has exceeded the rate limit.</p>
</li>
<li><p>If the user is within the limit, the app processes the request and interacts with the PostgreSQL Database. If they're over the limit, it sends back a “429 Too Many Requests” error.</p>
</li>
</ol>
<p>This architecture ensures that no matter which web server handles the request, the rate limit is checked against the same, shared data source.</p>
<h2 id="heading-step-1-how-to-define-the-infrastructure-with-terraform"><strong>Step 1: How to Define the Infrastructure with Terraform</strong></h2>
<p>Manually setting up multiple virtual machines can be tedious and prone to errors. That's why we use Terraform, an Infrastructure as Code (IaC) tool. It lets us define our entire infrastructure in configuration files.</p>
<p><strong>Note</strong>: You can skip this section if you just want to see the rate limiter in action and how it’s used.</p>
<p>Our <a target="_blank" href="https://github.com/sravankaruturi/system-design/blob/main/infra/main.tf">main.tf</a> file defines all the components of our system. Let's look at a key piece: the Redis VM.</p>
<pre><code class="lang-yaml"><span class="hljs-comment"># --- Redis Cache for Rate Limiter ---</span>
<span class="hljs-string">resource</span> <span class="hljs-string">"proxmox_vm_qemu"</span> <span class="hljs-string">"redis_cache"</span> {

    <span class="hljs-string">vmid</span>        <span class="hljs-string">=</span> <span class="hljs-number">130</span>
    <span class="hljs-string">name</span>        <span class="hljs-string">=</span> <span class="hljs-string">"redis-cache-rate-limiter"</span>
    <span class="hljs-string">target_node</span> <span class="hljs-string">=</span> <span class="hljs-string">"pve"</span>
    <span class="hljs-string">agent</span>       <span class="hljs-string">=</span> <span class="hljs-number">1</span>
    <span class="hljs-string">cores</span>       <span class="hljs-string">=</span> <span class="hljs-number">1</span>
    <span class="hljs-string">memory</span>      <span class="hljs-string">=</span> <span class="hljs-number">1024</span>
    <span class="hljs-comment"># ... cloud-init config ...</span>
    <span class="hljs-string">ipconfig0</span>  <span class="hljs-string">=</span> <span class="hljs-string">"ip=10.0.0.130/24,gw=10.0.0.1"</span>
    <span class="hljs-comment"># ... disk and network config ...</span>

    <span class="hljs-comment"># 1. Install Docker</span>
    <span class="hljs-string">provisioner</span> <span class="hljs-string">"remote-exec"</span> {
        <span class="hljs-string">inline</span> <span class="hljs-string">=</span> [
            <span class="hljs-string">"sleep 30; sudo apt-get update -y"</span>,
            <span class="hljs-string">"sudo apt-get install -y docker.io docker-compose"</span>,
            <span class="hljs-string">"sudo mkdir -p /opt/redis"</span>
        ]
    }

    <span class="hljs-comment"># 2. Upload docker-compose file</span>
    <span class="hljs-string">provisioner</span> <span class="hljs-string">"file"</span> {
         <span class="hljs-string">source</span>      <span class="hljs-string">=</span> <span class="hljs-string">"files/redis-docker-compose.yml"</span>
         <span class="hljs-string">destination</span> <span class="hljs-string">=</span> <span class="hljs-string">"/home/${var.ssh_user}/docker-compose.yml"</span>
    }

    <span class="hljs-comment"># 3. Move file and run docker-compose</span>
    <span class="hljs-string">provisioner</span> <span class="hljs-string">"remote-exec"</span> {
        <span class="hljs-string">inline</span> <span class="hljs-string">=</span> [
            <span class="hljs-string">"sudo mv /home/${var.ssh_user}/docker-compose.yml /opt/redis/docker-compose.yml"</span>,
            <span class="hljs-string">"cd /opt/redis &amp;&amp; sudo docker-compose up -d"</span>
        ]
    }
}
</code></pre>
<p>This block tells Terraform to create a <code>Proxmox QEMU virtual machine</code> with a specific IP address <code>(10.0.0.130)</code>. After the VM is created, it uses provisioners to connect via SSH and run commands. Here, it installs Docker, uploads our <code>redis-docker-compose.yml file</code>, and starts the Redis container.</p>
<p>The <code>redis-docker-compose.yml</code> itself is very straightforward:</p>
<pre><code class="lang-yaml"><span class="hljs-attr">version:</span> <span class="hljs-string">'3.8'</span>
<span class="hljs-attr">services:</span>
  <span class="hljs-attr">redis:</span>
    <span class="hljs-attr">image:</span> <span class="hljs-string">redis:latest</span>
    <span class="hljs-attr">container_name:</span> <span class="hljs-string">redis_cache</span>
    <span class="hljs-attr">restart:</span> <span class="hljs-string">always</span>
    <span class="hljs-attr">ports:</span>
      <span class="hljs-bullet">-</span> <span class="hljs-string">"6379:6379"</span>
    <span class="hljs-attr">volumes:</span>
      <span class="hljs-bullet">-</span> <span class="hljs-string">redisdata:/data</span>

<span class="hljs-attr">volumes:</span>
  <span class="hljs-attr">redisdata:</span>
</code></pre>
<p>This ensures we have a persistent, containerized Redis instance ready to serve our application. The Terraform configuration similarly defines our web servers, load balancer, and databases.</p>
<h2 id="heading-step-2-how-to-implement-the-rate-limiter-logic-in-python"><strong>Step 2: How to Implement the Rate Limiter Logic in Python</strong></h2>
<p>Now, for the heart of our system: the Python code that implements the rate limiting logic. We're using a sophisticated and memory-efficient algorithm called the Sliding Window Log.</p>
<p>The idea is simple: for each user, we keep a log of the timestamps of their recent requests. We store this log in a Redis Sorted Set.</p>
<p>Let's break down the code from <a target="_blank" href="https://github.com/sravankaruturi/system-design/blob/main/web-servers/app.py"><code>app.py</code></a>.</p>
<h3 id="heading-the-flask-appbeforerequest-hook"><strong>The Flask</strong> <code>@app.before_request</code> <strong>Hook</strong></h3>
<p>Flask allows us to run code before any request is handled by its intended view function. This is the perfect place to put our rate limiter.</p>
<pre><code class="lang-python"><span class="hljs-keyword">import</span> psycopg2
<span class="hljs-keyword">import</span> string
<span class="hljs-keyword">import</span> random
<span class="hljs-keyword">import</span> redis
<span class="hljs-keyword">import</span> time
<span class="hljs-keyword">from</span> flask <span class="hljs-keyword">import</span> Flask, request, redirect, jsonify

app = Flask(__name__)

<span class="hljs-comment"># --- Database Connection Details ---</span>
DB_HOST = <span class="hljs-string">"10.0.0.200"</span> 
DB_NAME = <span class="hljs-string">"urldb"</span>
DB_USER = <span class="hljs-string">"myuser"</span>
DB_PASS = <span class="hljs-string">"mypassword"</span>

REDIS_HOST = <span class="hljs-string">"10.0.0.130"</span> <span class="hljs-comment"># IP of your redis-cache-lxc</span>

<span class="hljs-comment"># --- Rate Limiter Settings ---</span>
RATE_LIMIT_COUNT = <span class="hljs-number">10</span>  <span class="hljs-comment"># 10 requests</span>
RATE_LIMIT_WINDOW = <span class="hljs-number">60</span> <span class="hljs-comment"># per 60 seconds</span>

<span class="hljs-comment"># Establish a reusable Redis connection</span>
redis_client = redis.Redis(host=REDIS_HOST, port=<span class="hljs-number">6379</span>, decode_responses=<span class="hljs-literal">True</span>)

<span class="hljs-meta">@app.before_request</span>
<span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">rate_limiter</span>():</span>
    <span class="hljs-comment"># Use the user's IP address as the key</span>
    <span class="hljs-comment"># In a real app, you'd handle proxies via request.environ.get('HTTP_X_FORWARDED_FOR', request.remote_addr)</span>
    key = <span class="hljs-string">f"rate_limit:<span class="hljs-subst">{request.remote_addr}</span>"</span>
    now = time.time()

    <span class="hljs-comment"># Use a Redis pipeline for atomic operations</span>
    pipe = redis_client.pipeline()
    <span class="hljs-comment"># 1. Add current request timestamp. The score and member are the same.</span>
    pipe.zadd(key, {str(now): now})
    <span class="hljs-comment"># 2. Remove all timestamps older than our window</span>
    pipe.zremrangebyscore(key, <span class="hljs-number">0</span>, now - RATE_LIMIT_WINDOW)
    <span class="hljs-comment"># 3. Get the count of remaining timestamps</span>
    pipe.zcard(key)
    <span class="hljs-comment"># 4. Set an expiration on the key so it cleans itself up</span>
    pipe.expire(key, RATE_LIMIT_WINDOW)

    <span class="hljs-comment"># Execute the pipeline and get the results</span>
    results = pipe.execute()
    request_count = results[<span class="hljs-number">2</span>] <span class="hljs-comment"># The result of the zcard command</span>

    <span class="hljs-keyword">if</span> request_count &gt; RATE_LIMIT_COUNT:
        <span class="hljs-comment"># Return a 429 Too Many Requests error</span>
        <span class="hljs-keyword">return</span> jsonify(error=<span class="hljs-string">"Rate limit exceeded"</span>), <span class="hljs-number">429</span>

<span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">get_db_connection</span>():</span>
    conn = psycopg2.connect(host=DB_HOST, dbname=DB_NAME, user=DB_USER, password=DB_PASS)
    <span class="hljs-keyword">return</span> conn

<span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">init_db</span>():</span>
    conn = get_db_connection()
    cur = conn.cursor()
    cur.execute(<span class="hljs-string">'''
        CREATE TABLE IF NOT EXISTS urls (
            id SERIAL PRIMARY KEY,
            short_code VARCHAR(6) UNIQUE NOT NULL,
            original_url TEXT NOT NULL
        );
    '''</span>)
    <span class="hljs-comment"># Check if the index exists before creating it</span>
    cur.execute(<span class="hljs-string">'''
        SELECT 1 FROM pg_class c JOIN pg_namespace n ON n.oid = c.relnamespace
        WHERE c.relname = 'idx_original_url' AND n.nspname = 'public';
    '''</span>)
    <span class="hljs-keyword">if</span> cur.fetchone() <span class="hljs-keyword">is</span> <span class="hljs-literal">None</span>:
        cur.execute(<span class="hljs-string">'CREATE INDEX idx_original_url ON urls (original_url);'</span>)
    conn.commit()
    cur.close()
    conn.close()

<span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">generate_short_code</span>(<span class="hljs-params">length=<span class="hljs-number">6</span></span>):</span>
    chars = string.ascii_letters + string.digits
    <span class="hljs-keyword">return</span> <span class="hljs-string">''</span>.join(random.choice(chars) <span class="hljs-keyword">for</span> _ <span class="hljs-keyword">in</span> range(length))

<span class="hljs-meta">@app.route("/", methods=['GET'])</span>
<span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">index</span>():</span>
    <span class="hljs-keyword">return</span> <span class="hljs-string">"URL Shortener is running!\n"</span>, <span class="hljs-number">200</span>

<span class="hljs-meta">@app.route('/shorten', methods=['POST'])</span>
<span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">shorten_url</span>():</span>
    original_url = request.form[<span class="hljs-string">'url'</span>]
    conn = get_db_connection()
    cur = conn.cursor()

    cur.execute(<span class="hljs-string">"SELECT short_code FROM urls WHERE original_url = %s"</span>, (original_url,))
    existing_url = cur.fetchone()

    <span class="hljs-keyword">if</span> existing_url:
        short_code = existing_url[<span class="hljs-number">0</span>]
    <span class="hljs-keyword">else</span>:
        short_code = generate_short_code()
        cur.execute(<span class="hljs-string">"INSERT INTO urls (short_code, original_url) VALUES (%s, %s)"</span>, (short_code, original_url))
        conn.commit()

    cur.close()
    conn.close()

    <span class="hljs-keyword">return</span> jsonify(short_url=<span class="hljs-string">f"/<span class="hljs-subst">{short_code}</span>"</span>)

<span class="hljs-meta">@app.route('/&lt;short_code&gt;')</span>
<span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">redirect_to_url</span>(<span class="hljs-params">short_code</span>):</span>
    conn = get_db_connection()
    cur = conn.cursor()
    cur.execute(<span class="hljs-string">"SELECT original_url FROM urls WHERE short_code = %s"</span>, (short_code,))
    url_record = cur.fetchone()
    cur.close()
    conn.close()

    <span class="hljs-keyword">if</span> url_record:
        <span class="hljs-keyword">return</span> redirect(url_record[<span class="hljs-number">0</span>])
    <span class="hljs-keyword">else</span>:
        <span class="hljs-keyword">return</span> <span class="hljs-string">"URL not found"</span>, <span class="hljs-number">404</span>

<span class="hljs-keyword">if</span> __name__ == <span class="hljs-string">'__main__'</span>:
    init_db() 
    app.run(host=<span class="hljs-string">'0.0.0.0'</span>, port=<span class="hljs-number">5000</span>)
</code></pre>
<h3 id="heading-how-it-works-step-by-step"><strong>How It Works, Step-by-Step</strong></h3>
<ol>
<li><p><strong>Identify the User:</strong> We create a unique Redis key for each user based on their IP address: <code>rate_limit:1.2.3.4</code>.</p>
</li>
<li><p><strong>Use a Pipeline:</strong> Network latency can be a bottleneck. A Redis pipeline bundles multiple commands into a single request-response cycle. This is much more efficient than sending them one by one. It also ensures the sequence of commands runs without being interrupted by commands from other clients.</p>
</li>
<li><p><strong>Log the Current Request (ZADD):</strong> We add the current timestamp (as a Unix epoch) to a sorted set. We use the timestamp for both the "member" and the "score," which allows us to easily filter by time.</p>
</li>
<li><p><strong>Clean Up Old Requests (ZREMRANGEBYSCORE):</strong> This is the "sliding window" part. We remove any timestamps from the set that are older than our <code>RATE_LIMIT_WINDOW</code> (60 seconds). This efficiently discards requests that are no longer relevant to the current rate limit period.</p>
</li>
<li><p><strong>Count the Recent Requests (ZCARD):</strong> We get the cardinality (the number of items) in the set. After the previous step, this number is our count of requests within the last 60 seconds.</p>
</li>
<li><p><strong>Mark the current record to expire (EXPIRE):</strong> We set an expiration on the key itself. If a user stops making requests, Redis will automatically delete their rate limit data after 60 seconds, preventing memory from filling up with old keys.</p>
</li>
<li><p><strong>Execute and Check:</strong> The <code>pipe.execute()</code> command sends all our bundled commands to Redis. We then check the result of our ZCARD command. If the count exceeds our <code>RATE_LIMIT_COUNT</code>, we immediately return a 429 error.</p>
</li>
</ol>
<p>This approach is incredibly fast and efficient. All the heavy lifting is done inside Redis, which is optimized for these kinds of operations.</p>
<h2 id="heading-step-3-containerizing-and-testing"><strong>Step 3: Containerizing and Testing</strong></h2>
<p>To deploy our application consistently across multiple VMs, we use Docker. Our Dockerfile is standard for a Python application: it starts from a Python image, installs dependencies from <code>requirements.txt</code>, copies the application code, and defines the command to run the app.</p>
<p>But how do we know it works? We test it!</p>
<p>We use <code>k6</code>, a modern load testing tool, to simulate heavy traffic. Our test script, <code>rate-test.js</code>, is designed specifically to verify the rate limiter.</p>
<pre><code class="lang-javascript"><span class="hljs-keyword">import</span> http <span class="hljs-keyword">from</span> <span class="hljs-string">'k6/http'</span>;
<span class="hljs-keyword">import</span> { check, sleep } <span class="hljs-keyword">from</span> <span class="hljs-string">'k6'</span>;

<span class="hljs-keyword">export</span> <span class="hljs-keyword">const</span> options = {
  <span class="hljs-attr">stages</span>: [
    <span class="hljs-comment">// Ramp up to 20 users. This is more than the 10 req/min limit</span>
    <span class="hljs-comment">// and should trigger the rate limiter.</span>
    { <span class="hljs-attr">duration</span>: <span class="hljs-string">'30s'</span>, <span class="hljs-attr">target</span>: <span class="hljs-number">20</span> },
    { <span class="hljs-attr">duration</span>: <span class="hljs-string">'1m'</span>, <span class="hljs-attr">target</span>: <span class="hljs-number">20</span> },
    { <span class="hljs-attr">duration</span>: <span class="hljs-string">'10s'</span>, <span class="hljs-attr">target</span>: <span class="hljs-number">0</span> },
  ],
};

<span class="hljs-keyword">export</span> <span class="hljs-keyword">default</span> <span class="hljs-function"><span class="hljs-keyword">function</span> (<span class="hljs-params"></span>) </span>{
  <span class="hljs-keyword">const</span> url = <span class="hljs-string">'http://10.0.0.100/shorten'</span>; <span class="hljs-comment">// The Load Balancer IP</span>
  <span class="hljs-keyword">const</span> payload = { <span class="hljs-attr">url</span>: <span class="hljs-string">`https://www.test-ratelimit-<span class="hljs-subst">${<span class="hljs-built_in">Math</span>.random()}</span>.com`</span> };

  <span class="hljs-keyword">const</span> res = http.post(url, payload);

  <span class="hljs-comment">// Check if the request was successful OR if it was correctly rate-limited</span>
  check(res, {
    <span class="hljs-string">'status is 200 (OK)'</span>: <span class="hljs-function">(<span class="hljs-params">r</span>) =&gt;</span> r.status === <span class="hljs-number">200</span>,
    <span class="hljs-string">'status is 429 (Too Many Requests)'</span>: <span class="hljs-function">(<span class="hljs-params">r</span>) =&gt;</span> r.status === <span class="hljs-number">429</span>,
  });

  sleep(<span class="hljs-number">1</span>);
}
</code></pre>
<p>The stages array configures the test to gradually increase the number of virtual users to 20. Since our rate limit is 10 requests per minute, this load is guaranteed to trigger the limiter.</p>
<p>The <code>check</code> function is the crucial part. It verifies that the server's response code is either 200 (meaning the request was successful) or 429 (meaning our rate limiter correctly blocked the request).</p>
<p>We should see about 10 of our requests go through of the around 1600 requests per minute that we send from the same IP address.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1758477504110/3a2f3f0f-8db0-453d-8900-42a6d0966a11.gif" alt="A gif showing the test run of the load testing script" class="image--center mx-auto" width="2640" height="1416" loading="lazy"></p>
<p>We can also check the logs on our webserver to see all the requests that were sent to it.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1758477959201/80a39d07-1c4e-4d45-8a42-9ac2ce6f360d.gif" alt="A small gif demonstrating Web Server Logs" class="image--center mx-auto" width="2640" height="1416" loading="lazy"></p>
<p>And if we look at the Redis cache/database itself, we’ll see all the keys and the TTL at which they expire.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1758478780827/6a07a2ee-0ad0-4b60-899f-d6a0453edbe7.png" alt="6a07a2ee-0ad0-4b60-899f-d6a0453edbe7" class="image--center mx-auto" width="3246" height="1834" loading="lazy"></p>
<p>This is how we rate limit applications using a Redis Cache Server.</p>
<p>Here are the complete files used in the project.</p>
<pre><code class="lang-yaml">    <span class="hljs-string">terraform</span> {
    <span class="hljs-string">required_providers</span> {
        <span class="hljs-string">proxmox</span> <span class="hljs-string">=</span> {
        <span class="hljs-string">source</span>  <span class="hljs-string">=</span> <span class="hljs-string">"telmate/proxmox"</span>
        <span class="hljs-string">version</span> <span class="hljs-string">=</span> <span class="hljs-string">"3.0.2-rc04"</span>
        }
    }
    }

    <span class="hljs-string">provider</span> <span class="hljs-string">"proxmox"</span> {
    <span class="hljs-string">pm_api_url</span>          <span class="hljs-string">=</span> <span class="hljs-string">var.proxmox_api_url</span>
    <span class="hljs-string">pm_api_token_id</span>     <span class="hljs-string">=</span> <span class="hljs-string">var.proxmox_api_token_id</span>
    <span class="hljs-string">pm_api_token_secret</span> <span class="hljs-string">=</span> <span class="hljs-string">var.proxmox_api_token_secret</span>
    <span class="hljs-string">pm_tls_insecure</span>     <span class="hljs-string">=</span> <span class="hljs-literal">true</span>
    }

    <span class="hljs-comment"># --- Shared Provisioner Connection Settings ---</span>
    <span class="hljs-string">locals</span> {
        <span class="hljs-string">connection_settings</span> <span class="hljs-string">=</span> {
            <span class="hljs-string">type</span>        <span class="hljs-string">=</span> <span class="hljs-string">"ssh"</span>
            <span class="hljs-string">user</span>        <span class="hljs-string">=</span> <span class="hljs-string">var.ssh_user</span>
            <span class="hljs-string">private_key</span> <span class="hljs-string">=</span> <span class="hljs-string">file(var.ssh_private_key_path)</span>
        }
    }

    <span class="hljs-comment"># --- Database LXC Containers ---</span>
    <span class="hljs-string">resource</span> <span class="hljs-string">"proxmox_lxc"</span> <span class="hljs-string">"postgres_db"</span> {
    <span class="hljs-string">hostname</span>     <span class="hljs-string">=</span> <span class="hljs-string">"postgres-db-lxc"</span>
    <span class="hljs-string">target_node</span>  <span class="hljs-string">=</span> <span class="hljs-string">var.target_node</span>
    <span class="hljs-string">ostemplate</span>   <span class="hljs-string">=</span> <span class="hljs-string">var.lxc_template</span>

    <span class="hljs-string">rootfs</span> {
        <span class="hljs-string">storage</span> <span class="hljs-string">=</span> <span class="hljs-string">"local-lvm"</span>
        <span class="hljs-string">size</span> <span class="hljs-string">=</span> <span class="hljs-string">"8G"</span>
    }

    <span class="hljs-string">password</span>     <span class="hljs-string">=</span> <span class="hljs-string">"admin"</span>
    <span class="hljs-string">unprivileged</span> <span class="hljs-string">=</span> <span class="hljs-literal">true</span>
    <span class="hljs-string">start</span>        <span class="hljs-string">=</span> <span class="hljs-literal">true</span>

    <span class="hljs-string">features</span> {
        <span class="hljs-string">nesting</span> <span class="hljs-string">=</span> <span class="hljs-literal">true</span>
        <span class="hljs-comment"># keyctl = true</span>
    }

    <span class="hljs-string">network</span> {
        <span class="hljs-string">name</span>   <span class="hljs-string">=</span> <span class="hljs-string">"eth0"</span>
        <span class="hljs-string">bridge</span> <span class="hljs-string">=</span> <span class="hljs-string">"vmbr0"</span>
        <span class="hljs-string">ip</span>     <span class="hljs-string">=</span> <span class="hljs-string">"10.0.0.200/24"</span>
        <span class="hljs-string">gw</span>     <span class="hljs-string">=</span> <span class="hljs-string">"10.0.0.1"</span>
    }

    <span class="hljs-string">provisioner</span> <span class="hljs-string">"remote-exec"</span> {
        <span class="hljs-string">connection</span> {
        <span class="hljs-string">type</span>        <span class="hljs-string">=</span> <span class="hljs-string">"ssh"</span>
        <span class="hljs-string">user</span>        <span class="hljs-string">=</span> <span class="hljs-string">var.ssh_user</span>
        <span class="hljs-string">private_key</span> <span class="hljs-string">=</span> <span class="hljs-string">file(var.ssh_private_key_path)</span>
        <span class="hljs-string">host</span>        <span class="hljs-string">=</span> <span class="hljs-string">split("/"</span>, <span class="hljs-string">self.network</span>[<span class="hljs-number">0</span>]<span class="hljs-string">.ip)</span>[<span class="hljs-number">0</span>]
        }
        <span class="hljs-string">inline</span> <span class="hljs-string">=</span> [
        <span class="hljs-string">"sudo apt-get update"</span>,
        <span class="hljs-string">"sudo apt-get install -y docker.io docker-compose python3-setuptools"</span>,
        <span class="hljs-string">"sudo usermod -aG docker ${var.ssh_user}"</span>,
        <span class="hljs-string">"sudo mkdir -p /opt/postgres"</span>,
        <span class="hljs-string">"sudo chown ${var.ssh_user}:${var.ssh_user} /opt/postgres"</span>
        ]
    }

    <span class="hljs-string">provisioner</span> <span class="hljs-string">"file"</span> {
        <span class="hljs-string">connection</span> {
        <span class="hljs-string">type</span>        <span class="hljs-string">=</span> <span class="hljs-string">"ssh"</span>
        <span class="hljs-string">user</span>        <span class="hljs-string">=</span> <span class="hljs-string">var.ssh_user</span>
        <span class="hljs-string">private_key</span> <span class="hljs-string">=</span> <span class="hljs-string">file(var.ssh_private_key_path)</span>
        <span class="hljs-string">host</span>        <span class="hljs-string">=</span> <span class="hljs-string">split("/"</span>, <span class="hljs-string">self.network</span>[<span class="hljs-number">0</span>]<span class="hljs-string">.ip)</span>[<span class="hljs-number">0</span>]
        }
        <span class="hljs-string">source</span>      <span class="hljs-string">=</span> <span class="hljs-string">"../databases/pg-docker-compose.yml"</span>
        <span class="hljs-string">destination</span> <span class="hljs-string">=</span> <span class="hljs-string">"/opt/postgres/docker-compose.yml"</span>
    }

    <span class="hljs-string">provisioner</span> <span class="hljs-string">"remote-exec"</span> {
        <span class="hljs-string">inline</span>     <span class="hljs-string">=</span> [<span class="hljs-string">"cd /opt/postgres &amp;&amp; sudo docker-compose up -d"</span>]

        <span class="hljs-string">connection</span> {
        <span class="hljs-string">type</span>        <span class="hljs-string">=</span> <span class="hljs-string">"ssh"</span>
        <span class="hljs-string">user</span>        <span class="hljs-string">=</span> <span class="hljs-string">var.ssh_user</span>
        <span class="hljs-string">private_key</span> <span class="hljs-string">=</span> <span class="hljs-string">file(var.ssh_private_key_path)</span>
        <span class="hljs-string">host</span>        <span class="hljs-string">=</span> <span class="hljs-string">split("/"</span>, <span class="hljs-string">self.network</span>[<span class="hljs-number">0</span>]<span class="hljs-string">.ip)</span>[<span class="hljs-number">0</span>]
        }
    }
    }

    <span class="hljs-string">resource</span> <span class="hljs-string">"proxmox_lxc"</span> <span class="hljs-string">"mongo_db"</span> {
        <span class="hljs-string">hostname</span>    <span class="hljs-string">=</span> <span class="hljs-string">"mongo-db-lxc"</span>
        <span class="hljs-string">target_node</span> <span class="hljs-string">=</span> <span class="hljs-string">var.target_node</span>
        <span class="hljs-string">ostemplate</span>  <span class="hljs-string">=</span> <span class="hljs-string">var.lxc_template</span>

        <span class="hljs-string">rootfs</span> {
            <span class="hljs-string">storage</span> <span class="hljs-string">=</span> <span class="hljs-string">"local-lvm"</span>
            <span class="hljs-string">size</span> <span class="hljs-string">=</span> <span class="hljs-string">"8G"</span>
        }

        <span class="hljs-string">password</span>    <span class="hljs-string">=</span> <span class="hljs-string">"admin"</span>
        <span class="hljs-string">unprivileged</span> <span class="hljs-string">=</span> <span class="hljs-literal">true</span>
        <span class="hljs-string">start</span>       <span class="hljs-string">=</span> <span class="hljs-literal">true</span>

        <span class="hljs-string">features</span> {
            <span class="hljs-string">nesting</span> <span class="hljs-string">=</span> <span class="hljs-literal">true</span>
        <span class="hljs-comment"># keyctl = true # Somehow this is blocking the apply command</span>
        }

        <span class="hljs-string">network</span> {
            <span class="hljs-string">name</span>   <span class="hljs-string">=</span> <span class="hljs-string">"eth0"</span>
            <span class="hljs-string">bridge</span> <span class="hljs-string">=</span> <span class="hljs-string">"vmbr0"</span>
            <span class="hljs-string">ip</span>     <span class="hljs-string">=</span> <span class="hljs-string">"10.0.0.210/24"</span>
            <span class="hljs-string">gw</span>     <span class="hljs-string">=</span> <span class="hljs-string">"10.0.0.1"</span>
        }

        <span class="hljs-comment"># Provisioners similar to postgres_db</span>
        <span class="hljs-string">provisioner</span> <span class="hljs-string">"remote-exec"</span> {
            <span class="hljs-string">connection</span> {
                <span class="hljs-string">type</span>        <span class="hljs-string">=</span> <span class="hljs-string">"ssh"</span>
                <span class="hljs-string">user</span>        <span class="hljs-string">=</span> <span class="hljs-string">var.ssh_user</span>
                <span class="hljs-string">private_key</span> <span class="hljs-string">=</span> <span class="hljs-string">file(var.ssh_private_key_path)</span>
                <span class="hljs-string">host</span>        <span class="hljs-string">=</span> <span class="hljs-string">split("/"</span>, <span class="hljs-string">self.network</span>[<span class="hljs-number">0</span>]<span class="hljs-string">.ip)</span>[<span class="hljs-number">0</span>]
            }
            <span class="hljs-string">inline</span> <span class="hljs-string">=</span> [
            <span class="hljs-string">"sudo apt-get update"</span>,
            <span class="hljs-string">"sudo apt-get install -y docker.io docker-compose python3-setuptools"</span>,
            <span class="hljs-string">"sudo usermod -aG docker ${var.ssh_user}"</span>,
            <span class="hljs-string">"sudo mkdir -p /opt/mongo"</span>,
            <span class="hljs-string">"sudo chown ${var.ssh_user}:${var.ssh_user} /opt/mongo"</span>
            ]
        }

        <span class="hljs-string">provisioner</span> <span class="hljs-string">"file"</span> {
            <span class="hljs-string">connection</span> {
            <span class="hljs-string">type</span>        <span class="hljs-string">=</span> <span class="hljs-string">"ssh"</span>
            <span class="hljs-string">user</span>        <span class="hljs-string">=</span> <span class="hljs-string">var.ssh_user</span>
            <span class="hljs-string">private_key</span> <span class="hljs-string">=</span> <span class="hljs-string">file(var.ssh_private_key_path)</span>
            <span class="hljs-string">host</span>        <span class="hljs-string">=</span> <span class="hljs-string">split("/"</span>, <span class="hljs-string">self.network</span>[<span class="hljs-number">0</span>]<span class="hljs-string">.ip)</span>[<span class="hljs-number">0</span>]
            }
            <span class="hljs-string">source</span>      <span class="hljs-string">=</span> <span class="hljs-string">"../databases/mongo-docker-compose.yml"</span>
            <span class="hljs-string">destination</span> <span class="hljs-string">=</span> <span class="hljs-string">"/opt/mongo/docker-compose.yml"</span>
        }

        <span class="hljs-string">provisioner</span> <span class="hljs-string">"remote-exec"</span> {
            <span class="hljs-string">connection</span> {
            <span class="hljs-string">type</span>        <span class="hljs-string">=</span> <span class="hljs-string">"ssh"</span>
            <span class="hljs-string">user</span>        <span class="hljs-string">=</span> <span class="hljs-string">var.ssh_user</span>
            <span class="hljs-string">private_key</span> <span class="hljs-string">=</span> <span class="hljs-string">file(var.ssh_private_key_path)</span>
            <span class="hljs-string">host</span>        <span class="hljs-string">=</span> <span class="hljs-string">split("/"</span>, <span class="hljs-string">self.network</span>[<span class="hljs-number">0</span>]<span class="hljs-string">.ip)</span>[<span class="hljs-number">0</span>]
            }
            <span class="hljs-string">inline</span>     <span class="hljs-string">=</span> [<span class="hljs-string">"cd /opt/mongo &amp;&amp; docker-compose up -d"</span>]
        }
    }

    <span class="hljs-comment"># --- Redis Cache for Rate Limiter ---</span>
    <span class="hljs-string">resource</span> <span class="hljs-string">"proxmox_vm_qemu"</span> <span class="hljs-string">"redis_cache"</span> {

        <span class="hljs-string">vmid</span>        <span class="hljs-string">=</span> <span class="hljs-number">130</span>
        <span class="hljs-string">name</span>        <span class="hljs-string">=</span> <span class="hljs-string">"redis-cache-rate-limiter"</span>
        <span class="hljs-string">target_node</span> <span class="hljs-string">=</span> <span class="hljs-string">"pve"</span>
        <span class="hljs-string">agent</span>       <span class="hljs-string">=</span> <span class="hljs-number">1</span>
        <span class="hljs-string">cpu</span> {
            <span class="hljs-string">cores</span>       <span class="hljs-string">=</span> <span class="hljs-number">1</span>
        }

        <span class="hljs-string">memory</span>      <span class="hljs-string">=</span> <span class="hljs-number">1024</span>
        <span class="hljs-string">boot</span>        <span class="hljs-string">=</span> <span class="hljs-string">"order=scsi0"</span> <span class="hljs-comment"># has to be the same as the OS disk of the template</span>
        <span class="hljs-string">clone</span>       <span class="hljs-string">=</span> <span class="hljs-string">"debian12-cloudinit"</span> <span class="hljs-comment"># The name of the template</span>
        <span class="hljs-string">scsihw</span>      <span class="hljs-string">=</span> <span class="hljs-string">"virtio-scsi-single"</span>
        <span class="hljs-string">vm_state</span>    <span class="hljs-string">=</span> <span class="hljs-string">"running"</span>
        <span class="hljs-string">automatic_reboot</span> <span class="hljs-string">=</span> <span class="hljs-literal">true</span>

        <span class="hljs-comment"># Cloud-Init configuration</span>
        <span class="hljs-string">cicustom</span>   <span class="hljs-string">=</span> <span class="hljs-string">"vendor=local:snippets/qemu-guest-agent.yml"</span> <span class="hljs-comment"># /var/lib/vz/snippets/qemu-guest-agent.yml</span>
        <span class="hljs-string">ciupgrade</span>  <span class="hljs-string">=</span> <span class="hljs-literal">true</span>
        <span class="hljs-string">nameserver</span> <span class="hljs-string">=</span> <span class="hljs-string">"1.1.1.1 8.8.8.8"</span>
        <span class="hljs-string">ipconfig0</span>  <span class="hljs-string">=</span> <span class="hljs-string">"ip=10.0.0.130/24,gw=10.0.0.1"</span>
        <span class="hljs-string">skip_ipv6</span>  <span class="hljs-string">=</span> <span class="hljs-literal">true</span>
        <span class="hljs-string">ciuser</span>     <span class="hljs-string">=</span> <span class="hljs-string">var.ssh_user</span>
        <span class="hljs-string">cipassword</span> <span class="hljs-string">=</span> <span class="hljs-string">var.ssh_password</span>
        <span class="hljs-string">sshkeys</span>    <span class="hljs-string">=</span> <span class="hljs-string">var.ssh_key</span>

        <span class="hljs-comment"># Most cloud-init images require a serial device for their display</span>
        <span class="hljs-string">serial</span> {
            <span class="hljs-string">id</span> <span class="hljs-string">=</span> <span class="hljs-number">0</span>
        }

        <span class="hljs-string">disks</span> {
            <span class="hljs-string">scsi</span> {
            <span class="hljs-string">scsi0</span> {
                <span class="hljs-comment"># We have to specify the disk from our template, else Terraform will think it's not supposed to be there</span>
                <span class="hljs-string">disk</span> {
                <span class="hljs-string">storage</span> <span class="hljs-string">=</span> <span class="hljs-string">"local-lvm"</span>
                <span class="hljs-comment"># The size of the disk should be at least as big as the disk in the template. If it's smaller, the disk will be recreated</span>
                <span class="hljs-string">size</span>    <span class="hljs-string">=</span> <span class="hljs-string">"5G"</span> 
                }
            }
            }
            <span class="hljs-string">ide</span> {
            <span class="hljs-comment"># Some images require a cloud-init disk on the IDE controller, others on the SCSI or SATA controller</span>
            <span class="hljs-string">ide1</span> {
                <span class="hljs-string">cloudinit</span> {
                <span class="hljs-string">storage</span> <span class="hljs-string">=</span> <span class="hljs-string">"local-lvm"</span>
                }
            }
            }
        }

        <span class="hljs-string">network</span> {
            <span class="hljs-string">id</span> <span class="hljs-string">=</span> <span class="hljs-number">0</span>
            <span class="hljs-string">bridge</span> <span class="hljs-string">=</span> <span class="hljs-string">"vmbr0"</span>
            <span class="hljs-string">model</span>  <span class="hljs-string">=</span> <span class="hljs-string">"virtio"</span>
        }

        <span class="hljs-string">connection</span> {
            <span class="hljs-string">type</span>        <span class="hljs-string">=</span> <span class="hljs-string">"ssh"</span>
            <span class="hljs-string">user</span>        <span class="hljs-string">=</span> <span class="hljs-string">var.ssh_user</span>
            <span class="hljs-string">private_key</span> <span class="hljs-string">=</span> <span class="hljs-string">file(var.ssh_private_key_path)</span>
            <span class="hljs-string">host</span>        <span class="hljs-string">=</span> <span class="hljs-string">"10.0.0.130"</span>
        }

        <span class="hljs-comment"># 1. Install Docker and create the final app directory</span>
        <span class="hljs-string">provisioner</span> <span class="hljs-string">"remote-exec"</span> {
            <span class="hljs-string">inline</span> <span class="hljs-string">=</span> [
                <span class="hljs-comment"># Wait for cloud-init to finish before doing anything else</span>
                <span class="hljs-string">"echo 'Waiting for cloud-init to finish...'"</span>,
                <span class="hljs-string">"while [ ! -f /var/lib/cloud/instance/boot-finished ]; do echo 'Still waiting...' &amp;&amp; sleep 1; done"</span>,
                <span class="hljs-string">"echo 'Cloud-init finished.'"</span>,

                <span class="hljs-comment"># Now, safely install packages</span>
                <span class="hljs-string">"sudo apt-get update -y"</span>,
                <span class="hljs-string">"sudo apt-get install -y docker.io docker-compose"</span>,
                <span class="hljs-string">"sudo mkdir -p /opt/redis"</span>,
            ]
        }

        <span class="hljs-string">provisioner</span> <span class="hljs-string">"file"</span> {
            <span class="hljs-string">source</span>      <span class="hljs-string">=</span> <span class="hljs-string">"../caching/redis-docker-compose.yml"</span>
            <span class="hljs-string">destination</span> <span class="hljs-string">=</span> <span class="hljs-string">"/home/${var.ssh_user}/docker-compose.yml"</span>
        }

        <span class="hljs-string">provisioner</span> <span class="hljs-string">"remote-exec"</span> {
            <span class="hljs-string">inline</span> <span class="hljs-string">=</span> [ <span class="hljs-string">"sudo mv /home/${var.ssh_user}/docker-compose.yml /opt/redis/docker-compose.yml"</span> ]
        }

        <span class="hljs-string">provisioner</span> <span class="hljs-string">"remote-exec"</span> {
            <span class="hljs-string">inline</span> <span class="hljs-string">=</span> [ <span class="hljs-string">"cd /opt/redis &amp;&amp; sudo docker-compose up -d"</span> ]
        }
    }

    <span class="hljs-string">resource</span> <span class="hljs-string">"proxmox_vm_qemu"</span> <span class="hljs-string">"web-servers"</span> {

        <span class="hljs-string">count</span> <span class="hljs-string">=</span> <span class="hljs-number">2</span>

        <span class="hljs-string">vmid</span>        <span class="hljs-string">=</span> <span class="hljs-string">count.index</span> <span class="hljs-string">+</span> <span class="hljs-number">150</span>
        <span class="hljs-string">name</span>        <span class="hljs-string">=</span> <span class="hljs-string">"web-server-tf-${count.index + 1}"</span>
        <span class="hljs-string">target_node</span> <span class="hljs-string">=</span> <span class="hljs-string">"pve"</span>
        <span class="hljs-string">agent</span>       <span class="hljs-string">=</span> <span class="hljs-number">1</span>
        <span class="hljs-string">cpu</span> {
            <span class="hljs-string">cores</span>       <span class="hljs-string">=</span> <span class="hljs-number">1</span>
        }
        <span class="hljs-string">memory</span>      <span class="hljs-string">=</span> <span class="hljs-number">1024</span>
        <span class="hljs-string">boot</span>        <span class="hljs-string">=</span> <span class="hljs-string">"order=scsi0"</span> <span class="hljs-comment"># has to be the same as the OS disk of the template</span>
        <span class="hljs-string">clone</span>       <span class="hljs-string">=</span> <span class="hljs-string">"debian12-cloudinit"</span> <span class="hljs-comment"># The name of the template</span>
        <span class="hljs-string">scsihw</span>      <span class="hljs-string">=</span> <span class="hljs-string">"virtio-scsi-single"</span>
        <span class="hljs-string">vm_state</span>    <span class="hljs-string">=</span> <span class="hljs-string">"running"</span>
        <span class="hljs-string">automatic_reboot</span> <span class="hljs-string">=</span> <span class="hljs-literal">true</span>

        <span class="hljs-comment"># Cloud-Init configuration</span>
        <span class="hljs-string">cicustom</span>   <span class="hljs-string">=</span> <span class="hljs-string">"vendor=local:snippets/qemu-guest-agent.yml"</span> <span class="hljs-comment"># /var/lib/vz/snippets/qemu-guest-agent.yml</span>
        <span class="hljs-string">ciupgrade</span>  <span class="hljs-string">=</span> <span class="hljs-literal">true</span>
        <span class="hljs-string">nameserver</span> <span class="hljs-string">=</span> <span class="hljs-string">"1.1.1.1 8.8.8.8"</span>
        <span class="hljs-string">ipconfig0</span>  <span class="hljs-string">=</span> <span class="hljs-string">"ip=10.0.0.${111 + count.index}/24,gw=10.0.0.1"</span>
        <span class="hljs-string">skip_ipv6</span>  <span class="hljs-string">=</span> <span class="hljs-literal">true</span>
        <span class="hljs-string">ciuser</span>     <span class="hljs-string">=</span> <span class="hljs-string">var.ssh_user</span>
        <span class="hljs-string">cipassword</span> <span class="hljs-string">=</span> <span class="hljs-string">var.ssh_password</span>
        <span class="hljs-string">sshkeys</span>    <span class="hljs-string">=</span> <span class="hljs-string">var.ssh_key</span>

        <span class="hljs-comment"># Most cloud-init images require a serial device for their display</span>
        <span class="hljs-string">serial</span> {
            <span class="hljs-string">id</span> <span class="hljs-string">=</span> <span class="hljs-number">0</span>
        }

        <span class="hljs-string">disks</span> {
            <span class="hljs-string">scsi</span> {
            <span class="hljs-string">scsi0</span> {
                <span class="hljs-comment"># We have to specify the disk from our template, else Terraform will think it's not supposed to be there</span>
                <span class="hljs-string">disk</span> {
                <span class="hljs-string">storage</span> <span class="hljs-string">=</span> <span class="hljs-string">"local-lvm"</span>
                <span class="hljs-comment"># The size of the disk should be at least as big as the disk in the template. If it's smaller, the disk will be recreated</span>
                <span class="hljs-string">size</span>    <span class="hljs-string">=</span> <span class="hljs-string">"5G"</span> 
                }
            }
            }
            <span class="hljs-string">ide</span> {
            <span class="hljs-comment"># Some images require a cloud-init disk on the IDE controller, others on the SCSI or SATA controller</span>
            <span class="hljs-string">ide1</span> {
                <span class="hljs-string">cloudinit</span> {
                <span class="hljs-string">storage</span> <span class="hljs-string">=</span> <span class="hljs-string">"local-lvm"</span>
                }
            }
            }
        }

        <span class="hljs-string">network</span> {
            <span class="hljs-string">id</span> <span class="hljs-string">=</span> <span class="hljs-number">0</span>
            <span class="hljs-string">bridge</span> <span class="hljs-string">=</span> <span class="hljs-string">"vmbr0"</span>
            <span class="hljs-string">model</span>  <span class="hljs-string">=</span> <span class="hljs-string">"virtio"</span>
        }

        <span class="hljs-string">connection</span> {
            <span class="hljs-string">type</span>        <span class="hljs-string">=</span> <span class="hljs-string">"ssh"</span>
            <span class="hljs-string">user</span>        <span class="hljs-string">=</span> <span class="hljs-string">var.ssh_user</span>
            <span class="hljs-string">private_key</span> <span class="hljs-string">=</span> <span class="hljs-string">file(var.ssh_private_key_path)</span>
            <span class="hljs-string">host</span>        <span class="hljs-string">=</span> <span class="hljs-string">"10.0.0.${111 + count.index}"</span>
        }

        <span class="hljs-comment"># 1. Install Docker and create the final app directory</span>
        <span class="hljs-string">provisioner</span> <span class="hljs-string">"remote-exec"</span> {
            <span class="hljs-string">inline</span> <span class="hljs-string">=</span> [
                <span class="hljs-comment"># Wait for cloud-init to finish before doing anything else</span>
                <span class="hljs-string">"echo 'Waiting for cloud-init to finish...'"</span>,
                <span class="hljs-string">"while [ ! -f /var/lib/cloud/instance/boot-finished ]; do echo 'Still waiting...' &amp;&amp; sleep 1; done"</span>,
                <span class="hljs-string">"echo 'Cloud-init finished.'"</span>,

                <span class="hljs-comment"># Now, safely install packages</span>
                <span class="hljs-string">"sudo apt-get update -y"</span>,
                <span class="hljs-string">"sudo apt-get install -y docker.io"</span>,
                <span class="hljs-string">"sudo mkdir -p /opt/app"</span>,
            ]
        }

        <span class="hljs-comment"># 2. Upload ONLY the necessary files to the user's home directory</span>
        <span class="hljs-string">provisioner</span> <span class="hljs-string">"file"</span> {
            <span class="hljs-string">source</span>      <span class="hljs-string">=</span> <span class="hljs-string">"../web-servers/app.py"</span>
            <span class="hljs-string">destination</span> <span class="hljs-string">=</span> <span class="hljs-string">"/home/${var.ssh_user}/app.py"</span>
        }
        <span class="hljs-string">provisioner</span> <span class="hljs-string">"file"</span> {
            <span class="hljs-string">source</span>      <span class="hljs-string">=</span> <span class="hljs-string">"../web-servers/Dockerfile"</span>
            <span class="hljs-string">destination</span> <span class="hljs-string">=</span> <span class="hljs-string">"/home/${var.ssh_user}/Dockerfile"</span>
        }
        <span class="hljs-string">provisioner</span> <span class="hljs-string">"file"</span> {
            <span class="hljs-string">source</span>      <span class="hljs-string">=</span> <span class="hljs-string">"../web-servers/requirements.txt"</span>
            <span class="hljs-string">destination</span> <span class="hljs-string">=</span> <span class="hljs-string">"/home/${var.ssh_user}/requirements.txt"</span>
        }

        <span class="hljs-comment"># 4. Move files from the home directory, build the image, and run the container</span>
        <span class="hljs-string">provisioner</span> <span class="hljs-string">"remote-exec"</span> {
            <span class="hljs-string">inline</span> <span class="hljs-string">=</span> [
                <span class="hljs-comment"># Move each file individually to be compatible with all shells</span>
                <span class="hljs-string">"sudo mv /home/${var.ssh_user}/app.py /opt/app/"</span>,
                <span class="hljs-string">"sudo mv /home/${var.ssh_user}/Dockerfile /opt/app/"</span>,
                <span class="hljs-string">"sudo mv /home/${var.ssh_user}/requirements.txt /opt/app/"</span>,

                <span class="hljs-comment"># Build the Docker image</span>
                <span class="hljs-string">"sudo docker build -t my-python-app /opt/app"</span>,

                <span class="hljs-comment"># Stop and remove any old containers to prevent conflicts</span>
                <span class="hljs-string">"sudo docker stop $(sudo docker ps -q --filter ancestor=my-python-app) 2&gt;/dev/null || true"</span>,
                <span class="hljs-string">"sudo docker rm $(sudo docker ps -aq --filter ancestor=my-python-app) 2&gt;/dev/null || true"</span>,

                <span class="hljs-comment"># Run the new container</span>
                <span class="hljs-string">"sudo docker run -d --restart always -p 80:5000 my-python-app"</span>
            ]
        }

        <span class="hljs-comment"># In your proxmox_vm_qemu "web_servers" resource</span>
        <span class="hljs-string">depends_on</span> <span class="hljs-string">=</span> [
            <span class="hljs-string">proxmox_lxc.postgres_db</span>,
            <span class="hljs-string">proxmox_vm_qemu.redis_cache</span>
        ]
    }

    <span class="hljs-comment"># --- Load Balancer VM ---</span>
    <span class="hljs-string">resource</span> <span class="hljs-string">"proxmox_vm_qemu"</span> <span class="hljs-string">"load_balancer"</span> {
        <span class="hljs-string">name</span>        <span class="hljs-string">=</span> <span class="hljs-string">"lb-1"</span>
        <span class="hljs-string">target_node</span> <span class="hljs-string">=</span> <span class="hljs-string">var.target_node</span>
        <span class="hljs-string">clone</span>       <span class="hljs-string">=</span> <span class="hljs-string">var.vm_template</span>
        <span class="hljs-string">agent</span>       <span class="hljs-string">=</span> <span class="hljs-number">1</span>
        <span class="hljs-string">cpu</span> {
            <span class="hljs-string">cores</span>       <span class="hljs-string">=</span> <span class="hljs-number">1</span>
        }
        <span class="hljs-string">memory</span>      <span class="hljs-string">=</span> <span class="hljs-number">512</span>
        <span class="hljs-string">boot</span>        <span class="hljs-string">=</span> <span class="hljs-string">"order=scsi0"</span> <span class="hljs-comment"># has to be the same as the OS disk of the template</span>
        <span class="hljs-string">scsihw</span>      <span class="hljs-string">=</span> <span class="hljs-string">"virtio-scsi-single"</span>
        <span class="hljs-string">vm_state</span>    <span class="hljs-string">=</span> <span class="hljs-string">"running"</span>
        <span class="hljs-string">automatic_reboot</span> <span class="hljs-string">=</span> <span class="hljs-literal">true</span>

        <span class="hljs-comment"># --- Add these lines for Cloud Init Drive ---</span>
                <span class="hljs-comment"># --- Add these lines for Cloud Init Drive ---</span>
        <span class="hljs-string">cicustom</span>   <span class="hljs-string">=</span> <span class="hljs-string">"vendor=local:snippets/qemu-guest-agent.yml"</span> <span class="hljs-comment"># /var/lib/vz/snippets/qemu-guest-agent.yml</span>
        <span class="hljs-string">ciupgrade</span>  <span class="hljs-string">=</span> <span class="hljs-literal">true</span>
        <span class="hljs-string">nameserver</span> <span class="hljs-string">=</span> <span class="hljs-string">"1.1.1.1 8.8.8.8"</span>
        <span class="hljs-string">ipconfig0</span>  <span class="hljs-string">=</span> <span class="hljs-string">"ip=10.0.0.100/24,gw=10.0.0.1"</span>
        <span class="hljs-string">skip_ipv6</span>  <span class="hljs-string">=</span> <span class="hljs-literal">true</span>
        <span class="hljs-string">ciuser</span>     <span class="hljs-string">=</span> <span class="hljs-string">var.ssh_user</span>
        <span class="hljs-string">cipassword</span> <span class="hljs-string">=</span> <span class="hljs-string">var.ssh_password</span>
        <span class="hljs-string">sshkeys</span>    <span class="hljs-string">=</span> <span class="hljs-string">var.ssh_key</span>

        <span class="hljs-comment"># Most cloud-init images require a serial device for their display</span>
        <span class="hljs-string">serial</span> {
            <span class="hljs-string">id</span> <span class="hljs-string">=</span> <span class="hljs-number">0</span>
        }

        <span class="hljs-string">disks</span> {
            <span class="hljs-string">scsi</span> {
            <span class="hljs-string">scsi0</span> {
                <span class="hljs-comment"># We have to specify the disk from our template, else Terraform will think it's not supposed to be there</span>
                <span class="hljs-string">disk</span> {
                <span class="hljs-string">storage</span> <span class="hljs-string">=</span> <span class="hljs-string">"local-lvm"</span>
                <span class="hljs-comment"># The size of the disk should be at least as big as the disk in the template. If it's smaller, the disk will be recreated</span>
                <span class="hljs-string">size</span>    <span class="hljs-string">=</span> <span class="hljs-string">"5G"</span> 
                }
            }
            }
            <span class="hljs-string">ide</span> {
            <span class="hljs-comment"># Some images require a cloud-init disk on the IDE controller, others on the SCSI or SATA controller</span>
            <span class="hljs-string">ide1</span> {
                <span class="hljs-string">cloudinit</span> {
                <span class="hljs-string">storage</span> <span class="hljs-string">=</span> <span class="hljs-string">"local-lvm"</span>
                }
            }
            }
        }

        <span class="hljs-string">network</span> {
            <span class="hljs-string">id</span> <span class="hljs-string">=</span> <span class="hljs-number">0</span>
            <span class="hljs-string">bridge</span> <span class="hljs-string">=</span> <span class="hljs-string">"vmbr0"</span>
            <span class="hljs-string">model</span>  <span class="hljs-string">=</span> <span class="hljs-string">"virtio"</span>
        }

        <span class="hljs-string">connection</span> {
            <span class="hljs-string">type</span>        <span class="hljs-string">=</span> <span class="hljs-string">"ssh"</span>
            <span class="hljs-string">user</span>        <span class="hljs-string">=</span> <span class="hljs-string">var.ssh_user</span>
            <span class="hljs-string">private_key</span> <span class="hljs-string">=</span> <span class="hljs-string">file(var.ssh_private_key_path)</span>
            <span class="hljs-string">host</span>        <span class="hljs-string">=</span> <span class="hljs-string">"10.0.0.100"</span>
        }

        <span class="hljs-comment"># Step 1: Install Nginx</span>
        <span class="hljs-string">provisioner</span> <span class="hljs-string">"remote-exec"</span> {
            <span class="hljs-string">inline</span> <span class="hljs-string">=</span> [
                <span class="hljs-comment"># Wait for cloud-init to finish before doing anything else</span>
                <span class="hljs-string">"echo 'Waiting for cloud-init to finish...'"</span>,
                <span class="hljs-string">"while [ ! -f /var/lib/cloud/instance/boot-finished ]; do echo 'Still waiting...' &amp;&amp; sleep 1; done"</span>,
                <span class="hljs-string">"echo 'Cloud-init finished.'"</span>,

                <span class="hljs-comment"># Now, safely install packages</span>
                <span class="hljs-string">"sudo apt-get update -y"</span>,
                <span class="hljs-string">"sudo apt-get install -y nginx"</span>
            ]
        }

        <span class="hljs-comment"># Step 2: Upload config to a temporary location</span>
        <span class="hljs-string">provisioner</span> <span class="hljs-string">"file"</span> {
            <span class="hljs-string">source</span>      <span class="hljs-string">=</span> <span class="hljs-string">"../web-servers/nginx.conf"</span>
            <span class="hljs-string">destination</span> <span class="hljs-string">=</span> <span class="hljs-string">"/tmp/nginx.conf"</span> <span class="hljs-comment"># Use /tmp instead</span>
        }

        <span class="hljs-comment"># Step 3: Use sudo to move the file to its final destination and reload nginx</span>
        <span class="hljs-string">provisioner</span> <span class="hljs-string">"remote-exec"</span> {
            <span class="hljs-string">inline</span> <span class="hljs-string">=</span> [
                <span class="hljs-string">"sudo mv /tmp/nginx.conf /etc/nginx/sites-available/default"</span>,
                <span class="hljs-string">"sudo systemctl reload nginx"</span>
            ]
        }
    }


    <span class="hljs-comment"># --- Load Tester VM ---</span>
    <span class="hljs-string">resource</span> <span class="hljs-string">"proxmox_vm_qemu"</span> <span class="hljs-string">"load_tester"</span> {
        <span class="hljs-string">name</span>        <span class="hljs-string">=</span> <span class="hljs-string">"load-tester-vm"</span>
        <span class="hljs-string">target_node</span> <span class="hljs-string">=</span> <span class="hljs-string">var.target_node</span>
        <span class="hljs-string">clone</span>       <span class="hljs-string">=</span> <span class="hljs-string">var.vm_template</span>
        <span class="hljs-string">agent</span>       <span class="hljs-string">=</span> <span class="hljs-number">1</span>
        <span class="hljs-string">cpu</span> {
            <span class="hljs-string">cores</span>       <span class="hljs-string">=</span> <span class="hljs-number">1</span>
        }
        <span class="hljs-string">memory</span>      <span class="hljs-string">=</span> <span class="hljs-number">1024</span>
        <span class="hljs-string">boot</span>        <span class="hljs-string">=</span> <span class="hljs-string">"order=scsi0"</span> <span class="hljs-comment"># has to be the same as the OS disk of the template</span>
        <span class="hljs-string">scsihw</span>      <span class="hljs-string">=</span> <span class="hljs-string">"virtio-scsi-single"</span>
        <span class="hljs-string">vm_state</span>    <span class="hljs-string">=</span> <span class="hljs-string">"running"</span>
        <span class="hljs-string">automatic_reboot</span> <span class="hljs-string">=</span> <span class="hljs-literal">true</span>

        <span class="hljs-comment"># --- Add these lines for Cloud Init Drive ---</span>
        <span class="hljs-string">cicustom</span>   <span class="hljs-string">=</span> <span class="hljs-string">"vendor=local:snippets/qemu-guest-agent.yml"</span> <span class="hljs-comment"># /var/lib/vz/snippets/qemu-guest-agent.yml</span>
        <span class="hljs-string">ciupgrade</span>  <span class="hljs-string">=</span> <span class="hljs-literal">true</span>
        <span class="hljs-string">nameserver</span> <span class="hljs-string">=</span> <span class="hljs-string">"1.1.1.1 8.8.8.8"</span>
        <span class="hljs-string">ipconfig0</span>  <span class="hljs-string">=</span> <span class="hljs-string">"ip=10.0.0.160/24,gw=10.0.0.1"</span>
        <span class="hljs-string">skip_ipv6</span>  <span class="hljs-string">=</span> <span class="hljs-literal">true</span>
        <span class="hljs-string">ciuser</span>     <span class="hljs-string">=</span> <span class="hljs-string">var.ssh_user</span>
        <span class="hljs-string">cipassword</span> <span class="hljs-string">=</span> <span class="hljs-string">var.ssh_password</span>
        <span class="hljs-string">sshkeys</span>    <span class="hljs-string">=</span> <span class="hljs-string">var.ssh_key</span>

        <span class="hljs-comment"># Most cloud-init images require a serial device for their display</span>
        <span class="hljs-string">serial</span> {
            <span class="hljs-string">id</span> <span class="hljs-string">=</span> <span class="hljs-number">0</span>
        }

        <span class="hljs-string">disks</span> {
            <span class="hljs-string">scsi</span> {
                <span class="hljs-string">scsi0</span> {
                    <span class="hljs-comment"># We have to specify the disk from our template, else Terraform will think it's not supposed to be there</span>
                    <span class="hljs-string">disk</span> {
                    <span class="hljs-string">storage</span> <span class="hljs-string">=</span> <span class="hljs-string">"local-lvm"</span>
                    <span class="hljs-comment"># The size of the disk should be at least as big as the disk in the template. If it's smaller, the disk will be recreated</span>
                    <span class="hljs-string">size</span>    <span class="hljs-string">=</span> <span class="hljs-string">"5G"</span> 
                    }
                }
            }

            <span class="hljs-string">ide</span> {
            <span class="hljs-comment"># Some images require a cloud-init disk on the IDE controller, others on the SCSI or SATA controller</span>
                <span class="hljs-string">ide1</span> {
                    <span class="hljs-string">cloudinit</span> {
                    <span class="hljs-string">storage</span> <span class="hljs-string">=</span> <span class="hljs-string">"local-lvm"</span>
                    }
                }
            }
        }

        <span class="hljs-string">network</span> {
            <span class="hljs-string">id</span> <span class="hljs-string">=</span> <span class="hljs-number">0</span>
            <span class="hljs-string">bridge</span> <span class="hljs-string">=</span> <span class="hljs-string">"vmbr0"</span>
            <span class="hljs-string">model</span>  <span class="hljs-string">=</span> <span class="hljs-string">"virtio"</span>
        }

        <span class="hljs-string">provisioner</span> <span class="hljs-string">"remote-exec"</span> {
            <span class="hljs-string">connection</span> {
                <span class="hljs-string">type</span>        <span class="hljs-string">=</span> <span class="hljs-string">"ssh"</span>
                <span class="hljs-string">user</span>        <span class="hljs-string">=</span> <span class="hljs-string">var.ssh_user</span>
                <span class="hljs-string">private_key</span> <span class="hljs-string">=</span> <span class="hljs-string">file(var.ssh_private_key_path)</span>
                <span class="hljs-string">host</span>        <span class="hljs-string">=</span> <span class="hljs-string">"10.0.0.160"</span>
            }
            <span class="hljs-string">inline</span> <span class="hljs-string">=</span> [
                <span class="hljs-comment"># Wait for cloud-init to finish</span>
                <span class="hljs-string">"echo 'Waiting for cloud-init to finish...'"</span>,
                <span class="hljs-string">"while [ ! -f /var/lib/cloud/instance/boot-finished ]; do echo 'Still waiting...' &amp;&amp; sleep 1; done"</span>,
                <span class="hljs-string">"echo 'Cloud-init finished.'"</span>,

                <span class="hljs-comment"># Install prerequisites</span>
                <span class="hljs-string">"sudo apt-get update -y"</span>,
                <span class="hljs-string">"sudo apt-get install -y gnupg curl"</span>,

                <span class="hljs-comment"># Add the k6 repository and key</span>
                <span class="hljs-string">"curl -sL https://dl.k6.io/key.gpg | sudo gpg --dearmor -o /usr/share/keyrings/k6-archive-keyring.gpg"</span>,
                <span class="hljs-string">"echo 'deb [signed-by=/usr/share/keyrings/k6-archive-keyring.gpg] https://dl.k6.io/deb stable main' | sudo tee /etc/apt/sources.list.d/k6.list"</span>,

                <span class="hljs-comment"># Install k6</span>
                <span class="hljs-string">"sudo apt-get update"</span>,
                <span class="hljs-string">"sudo apt-get install -y k6"</span>
            ]
        }

        <span class="hljs-string">provisioner</span> <span class="hljs-string">"file"</span> {
            <span class="hljs-string">connection</span> {
            <span class="hljs-string">type</span>        <span class="hljs-string">=</span> <span class="hljs-string">"ssh"</span>
            <span class="hljs-string">user</span>        <span class="hljs-string">=</span> <span class="hljs-string">var.ssh_user</span>
            <span class="hljs-string">private_key</span> <span class="hljs-string">=</span> <span class="hljs-string">file(var.ssh_private_key_path)</span>
            <span class="hljs-string">host</span>        <span class="hljs-string">=</span> <span class="hljs-string">"10.0.0.160"</span>
            }
            <span class="hljs-string">source</span>      <span class="hljs-string">=</span> <span class="hljs-string">"../load-testing/script.js"</span>
            <span class="hljs-string">destination</span> <span class="hljs-string">=</span> <span class="hljs-string">"/home/${var.ssh_user}/script.js"</span>
        }

        <span class="hljs-string">provisioner</span> <span class="hljs-string">"file"</span> {
            <span class="hljs-string">connection</span> {
            <span class="hljs-string">type</span>        <span class="hljs-string">=</span> <span class="hljs-string">"ssh"</span>
            <span class="hljs-string">user</span>        <span class="hljs-string">=</span> <span class="hljs-string">var.ssh_user</span>
            <span class="hljs-string">private_key</span> <span class="hljs-string">=</span> <span class="hljs-string">file(var.ssh_private_key_path)</span>
            <span class="hljs-string">host</span>        <span class="hljs-string">=</span> <span class="hljs-string">"10.0.0.160"</span>
            }
            <span class="hljs-string">source</span>      <span class="hljs-string">=</span> <span class="hljs-string">"../load-testing/rate-test.js"</span>
            <span class="hljs-string">destination</span> <span class="hljs-string">=</span> <span class="hljs-string">"/home/${var.ssh_user}/rate-test.js"</span>
        }

    }
</code></pre>
<h2 id="heading-conclusion"><strong>Conclusion</strong></h2>
<p>You've now seen how to build a complete, scalable, and resilient system that includes a crucial component for modern web applications: a distributed rate limiter.</p>
<p>We've covered the entire stack:</p>
<ul>
<li><p><strong>Infrastructure as Code</strong> with Terraform to define our virtual machines. (check out my repo <a target="_blank" href="https://github.com/sravankaruturi/system-design">here</a> for all the code and any updates I make).</p>
</li>
<li><p>A <strong>centralized, high-speed cache</strong> with Redis to store our rate limiting data.</p>
</li>
<li><p>An efficient <strong>Sliding Window Log algorithm</strong> implemented in Python with Flask.</p>
</li>
<li><p><strong>Containerization</strong> with Docker for consistent deployment.</p>
</li>
<li><p><strong>Load balancing</strong> with Nginx to distribute traffic.</p>
</li>
<li><p><strong>Load testing</strong> with k6 to validate our implementation.</p>
</li>
</ul>
<p>If you’d like to learn more of the concepts that are used when building large scale systems please follow me at <a class="user-mention" href="https://hashnode.com/@sravankaruturi">Sravan Karuturi</a>.</p>
 ]]>
                </content:encoded>
            </item>
        
            <item>
                <title>
                    <![CDATA[ Beginner’s Guide to Cloud Cybersecurity ]]>
                </title>
                <description>
                    <![CDATA[ Are you ready to become a digital guardian and launch your career on the frontlines of cloud security? We’re excited to announce that freeCodeCamp.org has just published a comprehensive Google Cloud Cybersecurity course on our YouTube channel. The co... ]]>
                </description>
                <link>https://www.freecodecamp.org/news/beginners-guide-to-cloud-cybersecurity/</link>
                <guid isPermaLink="false">6863fb880b9684674b11ec40</guid>
                
                    <category>
                        <![CDATA[ cybersecurity ]]>
                    </category>
                
                    <category>
                        <![CDATA[ youtube ]]>
                    </category>
                
                <dc:creator>
                    <![CDATA[ Beau Carnes ]]>
                </dc:creator>
                <pubDate>Tue, 01 Jul 2025 15:15:20 +0000</pubDate>
                <media:content url="https://cdn.hashnode.com/res/hashnode/image/upload/v1750284815294/c2ba70be-5147-4b56-b377-4ac40f53532e.png" medium="image" />
                <content:encoded>
                    <![CDATA[ <p>Are you ready to become a digital guardian and launch your career on the frontlines of cloud security? We’re excited to announce that freeCodeCamp.org has just published <a target="_blank" href="https://youtu.be/DrAZf4ZHqaM">a comprehensive Google Cloud Cybersecurity course</a> on our YouTube channel.</p>
<p>The course was developed by Google Cloud and helps people earn the Google Cloud Cybersecurity Certificate. This certificate program is your training ground to protect valuable digital assets from the ever-evolving world of cybercrime.</p>
<h3 id="heading-why-learn-cybersecurity-with-google-cloud">Why Learn Cybersecurity with Google Cloud?</h3>
<p>In our hyper-connected world, the security of digital infrastructure is important. Every organization, from startups to global enterprises, needs skilled professionals to defend against threats.</p>
<p>As a Cloud Security Analyst, you'll be the one to analyze threats, build defenses, and safeguard critical systems in the cloud. This program provides the essential skills to enter this high-growth field, blending expert-led instruction with immersive, hands-on Google Cloud challenges to build your expertise and impress future employers.</p>
<h3 id="heading-whats-inside-the-certificate-program">What’s Inside the Certificate Program?</h3>
<p>This program is a carefully structured path designed to take you from foundational concepts to advanced, real-world application. You'll not only learn technical skills like threat identification and risk management but also how to respond to incidents and communicate effectively during a crisis.</p>
<p>Here’s what you’ll learn across the five-course series:</p>
<p><strong>1. Introduction to Security Principles in Cloud Computing</strong> Start with the essentials of cybersecurity. You’ll explore the security lifecycle, understand the impact of digital transformation, and grasp key cloud computing concepts. This module introduces you to the common tools and responsibilities of an entry-level cybersecurity analyst.</p>
<p><strong>2. Strategies for Cloud Security Risk Management</strong> Dive into the critical process of managing risk in the cloud. This course covers widely-used risk management frameworks and industry standards like HIPAA, NIST CSF, and SOC. You'll learn to navigate the complexities of compliance and develop strategies to protect organizations.</p>
<p><strong>3. Cloud Security Risks: Identify and Protect Against Threats</strong> Explore the core principles of identity and access management within a cloud environment. You'll get hands-on with concepts like Authentication, Authorization, and Auditing (AAA), credential management, and techniques for identifying and mitigating system vulnerabilities.</p>
<p><strong>4. Detect, Respond, and Recover from Cloud Cybersecurity Attacks</strong> This course focuses on the operational side of security. You will develop crucial skills in monitoring, logging, and security alerting. You'll also learn proven techniques for mitigating attacks and customizing threat detection to stay ahead of adversaries.</p>
<p><strong>5. Put It All Together: Prepare for a Cloud Security Analyst Job</strong> Apply everything you’ve learned in a final capstone mission. This project simulates a real-world security scenario, challenging you to combine your skills in risk management, vulnerability identification, and incident response to solve a complex problem and build a standout portfolio piece.</p>
<h3 id="heading-start-your-journey-today">Start Your Journey Today</h3>
<p>This course provides the tools and training you need to succeed in the exciting and critical field of cloud cybersecurity. And it’s built around practical experience. It’s a thrilling mix of expert-led courses and immersive challenges through interactive labs on the Google Cloud Skills Boost platform.</p>
<p>These labs give you a chance to practice in a real cloud environment, reinforcing your learning with practical skills that employers are looking for.</p>
<p>Watch the <a target="_blank" href="https://youtu.be/DrAZf4ZHqaM">full course on the freeCodeCamp.org YouTube channel</a> and begin your journey into cybersecurity (10-hour watch).</p>
<div class="embed-wrapper">
        <iframe width="560" height="315" src="https://www.youtube.com/embed/DrAZf4ZHqaM" style="aspect-ratio: 16 / 9; width: 100%; height: auto;" title="YouTube video player" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share" referrerpolicy="strict-origin-when-cross-origin" allowfullscreen="" loading="lazy"></iframe></div>
 ]]>
                </content:encoded>
            </item>
        
            <item>
                <title>
                    <![CDATA[ How Cybercriminals Crack Your Passwords (And How to Stay One Step Ahead) ]]>
                </title>
                <description>
                    <![CDATA[ Passwords are the keys to your digital life  –  email, bank accounts, social media, and even your workplace systems. Unfortunately, they’re also one of the weakest links in cybersecurity.  Every year, billions of credentials are stolen and sold on th... ]]>
                </description>
                <link>https://www.freecodecamp.org/news/how-cybercriminals-crack-your-passwords-and-how-to-stay-one-step-ahead/</link>
                <guid isPermaLink="false">682b3930e99a22ce9f9199c8</guid>
                
                    <category>
                        <![CDATA[ Security ]]>
                    </category>
                
                    <category>
                        <![CDATA[ cybersecurity ]]>
                    </category>
                
                <dc:creator>
                    <![CDATA[ Manish Shivanandhan ]]>
                </dc:creator>
                <pubDate>Mon, 19 May 2025 13:59:12 +0000</pubDate>
                <media:content url="https://cdn.hashnode.com/res/hashnode/image/upload/v1747663111032/84a5cdc6-3d13-49e9-bc65-f2b7d0a51ad9.png" medium="image" />
                <content:encoded>
                    <![CDATA[ <p>Passwords are the keys to your digital life  –  email, bank accounts, social media, and even your workplace systems. Unfortunately, they’re also one of the weakest links in cybersecurity. </p>
<p><strong>Every year, billions of credentials are stolen and sold on the dark web.</strong> </p>
<p>Cybercriminals don’t always need advanced techniques to break into your account. Often, they rely on simple, automated methods that exploit human habits ,  like reusing passwords or choosing predictable ones. </p>
<p>Below are five of the most common ways attackers crack passwords and how you can protect yourself.</p>
<h2 id="heading-brute-force-attacks"><strong>Brute Force Attacks</strong></h2>
<p>Brute force attacks are one of the oldest hacking techniques still in use. </p>
<p>In this approach, attackers use a computer program to try every possible combination of characters until it finds the correct password. </p>
<p>While this may seem tedious, tools like Hydra, Medusa, or <a target="_blank" href="https://www.freecodecamp.org/news/crack-passwords-using-john-the-ripper-pentesting-tutorial/">John the Ripper</a> can attempt thousands  –  or even millions  –  of guesses per second.</p>
<p>For example, if your password is “test123,” a brute force tool will likely crack it in seconds. A 6-character password with only lowercase letters has 308 million possible combinations, which modern GPUs can process in minutes or less. </p>
<p>Your best defense against brute force is password length and complexity. </p>
<p>A random, 16-character password with mixed-case letters, numbers, and symbols is practically immune to brute force attacks with today’s hardware. </p>
<p>Using a password manager like NordPass, Bitwarden, or 1Password makes generating and storing such passwords easy and offers <a target="_blank" href="https://nordpass.com/secure-password/">strong password protection</a>. </p>
<h2 id="heading-dictionary-attacks"><strong>Dictionary Attacks</strong></h2>
<p>Unlike brute force, a dictionary attack narrows the search space by trying passwords from a precompiled list of commonly used words and phrases. </p>
<p>These lists often include leaked passwords from previous data breaches, popular sports teams, keyboard patterns like “qwerty” or “123456,” and even names or swear words. They are also called <a target="_blank" href="https://www.freecodecamp.org/news/the-power-of-wordlists-why-every-ethical-hacker-needs-one/">wordlists</a>. </p>
<p>Many people mistakenly believe that tweaking a common password  –  for instance, changing “password” to “P@ssw0rd!”  –  makes it secure. But dictionary attack tools account for these variations. </p>
<p>For instance, the tool <a target="_blank" href="https://www.kali.org/tools/crunch/">Crunch</a> allows attackers to generate wordlists with pattern-based rules, meaning “Welcome@123” is still a likely guess.</p>
<p>“123456”, “password”, and “qwerty” are still among the most common passwords in the world. Even passwords like “iloveyou” and “dragon” show up repeatedly.</p>
<p>To protect yourself, never use real words, names, or predictable patterns in your passwords. Instead, try using passphrases that are long, random, and unique  –  such as “truck-pillow-coffee-skyline” or a completely random string like “g6D@!rXplQ8#1zVn”. </p>
<p>Again, a password manager is the easiest way to maintain this level of randomness and uniqueness.</p>
<h2 id="heading-credential-stuffing"><strong>Credential Stuffing</strong></h2>
<p>Credential stuffing is one of the most successful and least sophisticated attack methods. It exploits one simple fact: people reuse passwords across multiple accounts. </p>
<p>When a site like LinkedIn or Dropbox gets breached and the passwords leak online, attackers take those stolen credentials and try them on other websites  – your email, Facebook, Netflix, or even bank portals.</p>
<p>This technique is highly automated. Attackers use bots to test thousands of username-password combinations across dozens of sites until they find a match. </p>
<p>Let’s say you used your Gmail password to sign up for a small forum years ago. That forum gets hacked, and your login details are exposed. If you’re still using that same password on Gmail, attackers now have a key to your inbox  –  which also means they may get access to all your other accounts via password reset links.</p>
<p>To defend against credential stuffing, use a unique password for every account. You don’t need to memorize all of them  –  just use a reputable password manager. </p>
<p>Also, turn on multi-factor authentication (MFA) wherever possible, so even if someone has your password, they still can’t log in without the second factor.</p>
<h2 id="heading-phishing-attacks"><strong>Phishing Attacks</strong></h2>
<p>Phishing isn’t a technical exploit  –  it’s a psychological one. </p>
<p>Instead of guessing your password, attackers trick you into giving it away. </p>
<p><a target="_blank" href="https://www.freecodecamp.org/news/how-to-recognize-phishing-email/">Phishing</a> often comes in the form of fake emails, text messages, or websites that look legitimate but are designed to steal your credentials.</p>
<p>For example, you might receive an email that looks like it’s from your bank, asking you to “verify your account.” The link takes you to a fake login page that captures your username and password the moment you enter them. </p>
<p>Tools like Evilginx and Modlishka can even bypass MFA by intercepting tokens in real time.</p>
<p>Phishing is widespread because it works. According to <a target="_blank" href="https://www.cisa.gov/">CISA</a>, phishing was the most common initial attack vector in 2022. And it’s getting more convincing with the use of AI to write emails, spoof sender addresses, and create realistic-looking websites.</p>
<p>To stay safe, never click on suspicious links or enter login details on a site you reached through an email. Always type URLs manually or use browser bookmarks for sensitive sites like banking or email. </p>
<p>Train yourself to spot red flags  –  like poor grammar, urgency, or mismatched sender names.</p>
<h2 id="heading-social-engineering-and-password-resets"><strong>Social Engineering and Password Resets</strong></h2>
<p>Sometimes, hackers don’t need technical skills at all  –  they just need to be convincing. </p>
<p><a target="_blank" href="https://www.freecodecamp.org/news/modern-social-engineering-cyberattacks/">Social engineering</a> involves manipulating people into giving up confidential information. One common tactic is calling customer support and pretending to be you. If the rep isn’t careful, they might reset your password or give access to your account.</p>
<p>This actually happened to <a target="_blank" href="https://www.wired.com/2012/08/apple-amazon-mat-honan-hacking/">tech journalist Mat Honan in 2012</a>, when hackers used social engineering to take over his Apple account. They then used it to wipe his phone, lock him out of email, and access other connected services.</p>
<p>Another trick is exploiting weak password reset systems. If a service allows you to reset your password by answering questions like “What’s your pet’s name?” or “Where were you born?”, attackers may already know the answers from your social media or data leaks.</p>
<p>To avoid this risk, limit what personal information you share online. </p>
<p>Use fake answers for password reset questions  –  just store them in your password manager. </p>
<p>And wherever possible, enable two-factor authentication using an app like Authy or Google Authenticator instead of relying on SMS, which can be intercepted via SIM swapping.</p>
<h2 id="heading-defense-is-easier-than-recovery"><strong>Defense is Easier Than Recovery</strong></h2>
<p>Cybercriminals don’t always need to “hack” their way in  –  they just need you to slip up. </p>
<p>The good news is that most password attacks rely on human error and predictable habits. By using a password manager, enabling multi-factor authentication, and staying alert to phishing attempts, you can block nearly all of these threats.</p>
<p>Think of your digital life like a house. Would you use the same key for your home, car, office, and locker? Would you leave it under the mat? That’s exactly what weak or reused passwords do online.</p>
<p>Stay one step ahead. Lock your digital doors properly  – and don’t give attackers the key.</p>
<p>Join the <a target="_blank" href="https://newsletter.stealthsecurity.sh/"><strong>Stealth Security Newsletter</strong></a> for more articles on Cybersecurity.</p>
 ]]>
                </content:encoded>
            </item>
        
    </channel>
</rss>
