<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>Forem: Evan Dolatowski</title>
    <description>The latest articles on Forem by Evan Dolatowski (@gnarlylasagna).</description>
    <link>https://forem.com/gnarlylasagna</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F1442427%2Fc9bf645f-5d7d-4b51-af3c-15ed54ae66f0.png</url>
      <title>Forem: Evan Dolatowski</title>
      <link>https://forem.com/gnarlylasagna</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://forem.com/feed/gnarlylasagna"/>
    <language>en</language>
    <item>
      <title>Building CloudSentinel: A Multi-Cloud Security Scanner</title>
      <dc:creator>Evan Dolatowski</dc:creator>
      <pubDate>Tue, 24 Mar 2026 16:49:34 +0000</pubDate>
      <link>https://forem.com/gnarlylasagna/building-cloudsentinel-a-multi-cloud-security-scanner-2li6</link>
      <guid>https://forem.com/gnarlylasagna/building-cloudsentinel-a-multi-cloud-security-scanner-2li6</guid>
      <description>&lt;h1&gt;
  
  
  Building CloudSentinel: A Multi-Cloud Security Scanner
&lt;/h1&gt;

&lt;p&gt;As a cybersecurity enthusiast and developer, I wanted to build a tool that could &lt;strong&gt;deploy vulnerable cloud infrastructure, scan for misconfigurations, and report findings across AWS, Azure, and GCP&lt;/strong&gt;. The goal was to simulate real-world security risks and demonstrate automation, planning, and multi-cloud proficiency.&lt;/p&gt;

&lt;p&gt;This blog post walks through the &lt;strong&gt;planning, development, and implementation&lt;/strong&gt; of CloudSentinel — highlighting how I structured the project, solved problems, and implemented cloud-agnostic scanning.&lt;/p&gt;




&lt;h2&gt;
  
  
  Project Motivation
&lt;/h2&gt;

&lt;p&gt;Many cloud security tutorials focus on a single provider, and most security tools are either too high-level or not hands-on. I wanted a system that could:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Deploy test infrastructure with intentional vulnerabilities&lt;/li&gt;
&lt;li&gt;Scan for misconfigurations automatically&lt;/li&gt;
&lt;li&gt;Aggregate findings and calculate risk scores&lt;/li&gt;
&lt;li&gt;Work consistently across &lt;strong&gt;AWS, Azure, and GCP&lt;/strong&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The resulting tool, &lt;strong&gt;CloudSentinel&lt;/strong&gt;, achieves all of this with a &lt;strong&gt;modular Python CLI and Terraform-backed infrastructure&lt;/strong&gt;.&lt;/p&gt;




&lt;h2&gt;
  
  
  Planning &amp;amp; Project Structure
&lt;/h2&gt;

&lt;p&gt;I started with a clear &lt;strong&gt;step-by-step plan&lt;/strong&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;mkdir &lt;/span&gt;cloudsentinel
&lt;span class="nb"&gt;cd &lt;/span&gt;cloudsentinel
git init
&lt;span class="nb"&gt;mkdir &lt;/span&gt;terraform scanner cli reports docs
&lt;span class="nb"&gt;touch &lt;/span&gt;README.md .gitignore
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Key design decisions:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;CLI layer (cli/): handles user commands (deploy, scan, report, destroy, status)&lt;/li&gt;
&lt;li&gt;Scanner engine (scanner/): modular checks per cloud provider&lt;/li&gt;
&lt;li&gt;Terraform directories (terraform/aws, terraform/azure, terraform/gcp): separate deployments for each cloud&lt;/li&gt;
&lt;li&gt;Reports (reports/): structured JSON and CSV outputs&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This separation of concerns ensures scalability and maintainability.&lt;/p&gt;




&lt;h2&gt;
  
  
  AWS Implementation Highlights
&lt;/h2&gt;

&lt;p&gt;I began with AWS to prove the concept:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Terraform deployment:

&lt;ul&gt;
&lt;li&gt;EC2 instance with SSH open to the world
Security group vulnerabilities&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;Scanner checks:

&lt;ul&gt;
&lt;li&gt;SSH open to 0.0.0.0/0&lt;/li&gt;
&lt;li&gt;Open ports&lt;/li&gt;
&lt;li&gt;Public S3 buckets&lt;/li&gt;
&lt;li&gt;Over-permissive IAM roles&lt;/li&gt;
&lt;li&gt;CloudTrail misconfigurations&lt;/li&gt;
&lt;li&gt;Unencrypted EBS volumes&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;/ul&gt;

&lt;p&gt;Example Python scanner module:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;scan_open_ssh&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;
    &lt;span class="n"&gt;result&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;subprocess&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;run&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;aws&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;ec2&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;describe-security-groups&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;--query&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;SecurityGroups[*].IpPermissions&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;--output&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;json&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
        &lt;span class="n"&gt;capture_output&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="bp"&gt;True&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;text&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="bp"&gt;True&lt;/span&gt;
    &lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;permissions&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;json&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;loads&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;result&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;stdout&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;findings&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[]&lt;/span&gt;
    &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;group&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;permissions&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;rule&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;group&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;rule&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;FromPort&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="mi"&gt;22&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
                &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;ip_range&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;rule&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;IpRanges&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;[]):&lt;/span&gt;
                    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;ip_range&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;CidrIp&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;0.0.0.0/0&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
                        &lt;span class="n"&gt;findings&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;append&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;issue&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;SSH open to internet&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;severity&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;HIGH&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;})&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;findings&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  Scanner Engine &amp;amp; Modular Architecture
&lt;/h2&gt;

&lt;p&gt;To handle multiple checks without clutter, I refactored the scanner into a modular engine:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;scanner/
├── engine.py       # orchestrator
├── aws_scanner.py
├── azure_scanner.py
├── gcp_scanner.py
└── checks/
    ├── open_ssh.py
    ├── open_ports.py
    └── public_storage.py
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The engine runs all checks and aggregates findings:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;run_all_checks&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;
    &lt;span class="n"&gt;findings&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[]&lt;/span&gt;
    &lt;span class="n"&gt;findings&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;extend&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;run_aws_checks&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt;
    &lt;span class="n"&gt;findings&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;extend&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;run_azure_checks&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt;
    &lt;span class="n"&gt;findings&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;extend&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;run_gcp_checks&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;findings&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This design allows scalable expansion to new checks or cloud providers.&lt;/p&gt;




&lt;h2&gt;
  
  
  Azure Integration
&lt;/h2&gt;

&lt;p&gt;Azure required service principal authentication:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;az login &lt;span class="nt"&gt;--service-principal&lt;/span&gt; &lt;span class="nt"&gt;-u&lt;/span&gt; &amp;lt;appId&amp;gt; &lt;span class="nt"&gt;-p&lt;/span&gt; &amp;lt;password&amp;gt; &lt;span class="nt"&gt;--tenant&lt;/span&gt; &amp;lt;tenantId&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Terraform resources included:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Resource Group&lt;/li&gt;
&lt;li&gt;Virtual Network and Subnet&lt;/li&gt;
&lt;li&gt;Vulnerable Network Security Group (open to all inbound traffic)&lt;/li&gt;
&lt;li&gt;Storage Account with public blob access&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Scanner modules for Azure:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;nsg_open_ssh.py → detects insecure NSG rules&lt;/li&gt;
&lt;li&gt;public_storage.py → detects public blobs&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Status and report functions were updated to include Azure:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="o"&gt;[&lt;/span&gt;CloudSentinel] Checking Azure resources...
Resource Groups:    1
Virtual Machines:   0
Network Security Groups: 1
Storage Accounts:   1
Cloud Environment Status: RESOURCES REMAIN ⚠️
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Scan results:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="o"&gt;[&lt;/span&gt;AWS] SSH open to internet &lt;span class="o"&gt;(&lt;/span&gt;HIGH&lt;span class="o"&gt;)&lt;/span&gt;
&lt;span class="o"&gt;[&lt;/span&gt;AZURE] Public blob access enabled &lt;span class="o"&gt;(&lt;/span&gt;HIGH&lt;span class="o"&gt;)&lt;/span&gt;
Risk Score &lt;span class="o"&gt;(&lt;/span&gt;0-10&lt;span class="o"&gt;)&lt;/span&gt;: 6.0
Overall Risk Level: MEDIUM
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  GCP Integration
&lt;/h2&gt;

&lt;p&gt;For GCP, I used a service account with Terraform:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;gcloud iam service-accounts create cloudsentinel-sa
gcloud projects add-iam-policy-binding YOUR_PROJECT_ID &lt;span class="se"&gt;\&lt;/span&gt;
    &lt;span class="nt"&gt;--member&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"serviceAccount:cloudsentinel-sa@YOUR_PROJECT_ID.iam.gserviceaccount.com"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
    &lt;span class="nt"&gt;--role&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"roles/editor"&lt;/span&gt;
gcloud iam service-accounts keys create ~/cloudsentinel-sa.json &lt;span class="se"&gt;\&lt;/span&gt;
    &lt;span class="nt"&gt;--iam-account&lt;/span&gt; cloudsentinel-sa@YOUR_PROJECT_ID.iam.gserviceaccount.com
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Terraform resources included:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Compute Engine VM with public SSH&lt;/li&gt;
&lt;li&gt;GCS bucket with public access&lt;/li&gt;
&lt;li&gt;Firewall rules for open ports&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Scanner checks mirrored AWS/Azure:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;run_gcp_checks&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;
    &lt;span class="c1"&gt;# Check VMs, Storage, and Networks
&lt;/span&gt;    &lt;span class="bp"&gt;...&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;CLI fully supports deploy, scan, report, destroy, status for GCP just like AWS and Azure.&lt;/p&gt;




&lt;h2&gt;
  
  
  Reporting &amp;amp; Risk Scoring
&lt;/h2&gt;

&lt;p&gt;CloudSentinel generates structured JSON and CSV reports, including:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Vulnerability list per cloud&lt;/li&gt;
&lt;li&gt;Severity counts (CRITICAL, HIGH, MEDIUM, LOW)&lt;/li&gt;
&lt;li&gt;Normalized risk score (0–10)&lt;/li&gt;
&lt;li&gt;Summary for SOC analysts or auditors&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Example summary output:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="o"&gt;---&lt;/span&gt; &lt;span class="n"&gt;Security&lt;/span&gt; &lt;span class="n"&gt;Summary&lt;/span&gt; &lt;span class="o"&gt;---&lt;/span&gt;
&lt;span class="n"&gt;CRITICAL&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;
&lt;span class="n"&gt;HIGH&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;     &lt;span class="mi"&gt;3&lt;/span&gt;
&lt;span class="n"&gt;MEDIUM&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;   &lt;span class="mi"&gt;2&lt;/span&gt;
&lt;span class="n"&gt;LOW&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;      &lt;span class="mi"&gt;0&lt;/span&gt;
&lt;span class="n"&gt;Risk&lt;/span&gt; &lt;span class="nc"&gt;Score &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt; &lt;span class="mf"&gt;7.33&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  Lessons Learned
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Terraform differs slightly across cloud providers; planning is critical&lt;/li&gt;
&lt;li&gt;Modular scanner architecture enables scalable vulnerability detection&lt;/li&gt;
&lt;li&gt;Automation with Python CLI unifies multi-cloud operations&lt;/li&gt;
&lt;li&gt;Validating destroy operations prevents unintended cloud costs&lt;/li&gt;
&lt;li&gt;Structured reporting mirrors real-world SOC workflows&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  Next Steps
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Expand scanner checks for Azure and GCP&lt;/li&gt;
&lt;li&gt;Integrate with SIEM tools like Splunk&lt;/li&gt;
&lt;li&gt;Build a web dashboard for vulnerability visualization&lt;/li&gt;
&lt;li&gt;CI/CD pipeline to automate scans and reports&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;CloudSentinel demonstrates the full lifecycle of cloud security testing:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Multi-cloud deployment with Terraform&lt;/li&gt;
&lt;li&gt;Automated scanning with modular Python&lt;/li&gt;
&lt;li&gt;Risk scoring and structured reporting&lt;/li&gt;
&lt;li&gt;Cleanup and status verification&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This project showcases problem-solving, planning, and hands-on cloud security skills, ready to impress potential employers and security teams.&lt;/p&gt;

&lt;p&gt;Check out the project on GitHub: &lt;a href="https://github.com/GnarlyLasagna/cloud-sentinel" rel="noopener noreferrer"&gt;CloudSentinel Git Repo&lt;/a&gt;&lt;/p&gt;

</description>
      <category>terraform</category>
      <category>aws</category>
      <category>azure</category>
      <category>gcp</category>
    </item>
    <item>
      <title>Building a Virtualized Cybersecurity Lab: Introducing an Adversary with Kali Linux</title>
      <dc:creator>Evan Dolatowski</dc:creator>
      <pubDate>Mon, 15 Dec 2025 01:12:37 +0000</pubDate>
      <link>https://forem.com/gnarlylasagna/building-a-virtualized-cybersecurity-lab-introducing-an-adversary-with-kali-linux-32ab</link>
      <guid>https://forem.com/gnarlylasagna/building-a-virtualized-cybersecurity-lab-introducing-an-adversary-with-kali-linux-32ab</guid>
      <description>&lt;p&gt;This is the Fourth blog post in a series about building my Virtual home lab network as a Cybersecurity Student.&lt;/p&gt;

&lt;p&gt;Originally, I planned to integrate a physical Kali Linux system via VPN. After encountering unnecessary operational complexity, I pivoted to deploying a Kali Linux virtual machine directly inside the lab-LAN. This approach improves reproducibility, safety, and visibility while still supporting realistic attack simulation.&lt;/p&gt;

&lt;p&gt;This design choice also reflects many real-world incidents, where adversarial activity originates from inside a trusted or semi-trusted network rather than directly from the public internet.&lt;/p&gt;

&lt;h2&gt;
  
  
  Lab Architecture Reminder
&lt;/h2&gt;

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

&lt;p&gt;As a recap, this virtualized cybersecurity lab consists of:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;pfSense firewall acting as router, DHCP server, and DNS forwarder&lt;/li&gt;
&lt;li&gt;Lab-NAT and Lab-LAN virtual networks&lt;/li&gt;
&lt;li&gt;Windows Server 2022 (Active Directory Domain Controller)&lt;/li&gt;
&lt;li&gt;Windows 11 domain-joined workstation&lt;/li&gt;
&lt;li&gt;Ubuntu Desktop (Splunk SIEM)&lt;/li&gt;
&lt;li&gt;Ubuntu Server (infrastructure services)&lt;/li&gt;
&lt;li&gt;Kali Linux attack VM (internal adversary)
At this stage, the environment mirrors a small enterprise network with centralized identity, logging, and segmented routing.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Process Overview
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Introducing Kali as an Internal Adversary
&lt;/h3&gt;

&lt;p&gt;The goal of this step is to deploy a controlled attack system that lives entirely inside the lab-LAN and can generate observable activity without exposing the host system or external networks.&lt;/p&gt;

&lt;h3&gt;
  
  
  Step 1 — Creating the Kali Linux VM
&lt;/h3&gt;

&lt;p&gt;The Kali Linux VM is deployed using the same virt-install and Virt-Manager workflow used throughout the lab:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;sudo virt-install \
  --name kali \
  --ram 4096 \
  --vcpus 2 \
  --disk path=/var/lib/libvirt/images/kali.qcow2,size=20,format=qcow2 \
  --cdrom /var/lib/libvirt/boot/kali-linux.iso \
  --network network=lab-lan,model=virtio \
  --os-variant ubuntu22.04 \
  --graphics spice \
  --boot uefi
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



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

&lt;p&gt;Notes:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;The Kali VM is connected only to the lab-LAN, keeping it isolated from the host and internet.&lt;/li&gt;
&lt;li&gt;A static IP is recommended to ensure repeatable attack simulations and predictable logging.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Step 2 — Configure a Static IP (Optional but Recommended)
&lt;/h3&gt;

&lt;p&gt;Manually assigning a static IP simplifies Splunk searches and detection logic.&lt;/p&gt;

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

&lt;h3&gt;
  
  
  Step 3 — Integrating Kali with Splunk
&lt;/h3&gt;

&lt;p&gt;With centralized logging already in place, the next step is to ensure activity generated from Kali is observable.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;sudo apt install rsyslog
echo '*.* @10.0.0.52:514' | sudo tee /etc/rsyslog.d/60-splunk.conf
sudo systemctl restart rsyslog
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Logs are forwarded using UDP syslog to remain consistent with the existing Ubuntu and pfSense log ingestion pipeline.&lt;/p&gt;

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

&lt;p&gt;In a production environment, forwarding logs from an attack system would be carefully scoped. In this lab, full visibility is intentional to support learning and detection validation.&lt;/p&gt;

&lt;p&gt;This ensures that authentication attempts, network activity, and simulated attacks from Kali appear in the SIEM alongside logs from Windows, Linux, and pfSense.&lt;/p&gt;

&lt;h3&gt;
  
  
  Validating Network Connectivity and Routing
&lt;/h3&gt;

&lt;p&gt;A. Full Network Discovery from Kali&lt;/p&gt;

&lt;p&gt;From the Kali VM:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;ip a
ip route
nmap -sn 10.0.0.0/24
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



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

&lt;p&gt;What this proves:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Kali is correctly attached to the lab-LAN&lt;/li&gt;
&lt;li&gt;pfSense is routing traffic as designed&lt;/li&gt;
&lt;li&gt;All domain members are reachable&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;📸 Screenshot: Nmap host discovery showing pfSense, DC, Windows 11, and Splunk&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;SOC Lens:&lt;/strong&gt;&lt;br&gt;
Internal host discovery is a common reconnaissance technique and often triggers investigation due to unusual scanning behavior.&lt;/p&gt;

&lt;p&gt;Tie-in to Part 1:&lt;br&gt;
This validates the segmentation and routing configured in the pfSense-based network.&lt;/p&gt;
&lt;h3&gt;
  
  
  B. Controlled Port Scanning
&lt;/h3&gt;


&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;nmap -sS -p 1-1024 10.0.0.25
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


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

&lt;p&gt;Immediately after, search in Splunk:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;index=* host=winserver*
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This query confirms that scan activity from Kali is generating security-relevant events on the Windows host and that those events are centrally searchable.&lt;/p&gt;

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

&lt;p&gt;What this proves:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Attack traffic is successfully generated&lt;/li&gt;
&lt;li&gt;Host firewall behavior is visible&lt;/li&gt;
&lt;li&gt;Telemetry is ingested centrally&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Tie-in to Part 3:&lt;br&gt;
This confirms that network activity appears in Splunk in near real time.&lt;/p&gt;
&lt;h3&gt;
  
  
  Observing Identity and Authentication Activity
&lt;/h3&gt;
&lt;h3&gt;
  
  
  A. Kerberos Authentication Validation
&lt;/h3&gt;


&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;kinit administrator@LAB.LOCAL
klist
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


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

&lt;p&gt;What this proves:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Active Directory is functional&lt;/li&gt;
&lt;li&gt;DNS, Kerberos, and time synchronization are working&lt;/li&gt;
&lt;li&gt;Cross-platform Linux and Windows identity integration is functioning as expected&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;In Splunk, search for:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Event ID 4768 (Kerberos TGT request)&lt;/li&gt;
&lt;li&gt;Event ID 4624 (successful logon)&lt;/li&gt;
&lt;/ul&gt;

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

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

&lt;p&gt;Tie-in to Part 2:&lt;br&gt;
Identity remains the primary control plane of the environment.&lt;/p&gt;
&lt;h3&gt;
  
  
  DNS and Name Resolution Validation
&lt;/h3&gt;


&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;nslookup lab.local
nslookup winserver.lab.local
nslookup google.com
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;What this proves:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Internal DNS resolution via Active Directory&lt;/li&gt;
&lt;li&gt;Name-based attack paths are observable&lt;/li&gt;
&lt;/ul&gt;

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

&lt;p&gt;In Splunk:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Review DNS query logs from the Domain Controller&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Tie-in to Parts 2 &amp;amp; 3:&lt;br&gt;
DNS underpins authentication, monitoring, and attack visibility.&lt;/p&gt;
&lt;h3&gt;
  
  
  Generating Detectable Attack Traffic
&lt;/h3&gt;

&lt;p&gt;Visibility and Detection Validation&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;nmap -A 10.0.0.0/24
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



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

&lt;p&gt;In Splunk:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;index=* sourcetype=sysmon EventCode=3
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Observed:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Network connections&lt;/li&gt;
&lt;li&gt;Process names&lt;/li&gt;
&lt;li&gt;Source IP = Kali VM&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This confirms the environment is not just functional — it is observable.&lt;/p&gt;

&lt;h2&gt;
  
  
  Key Takeaways / Lessons Learned
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Introducing a Kali Linux attack VM completes the lab by adding an adversarial perspective to an enterprise-style environment&lt;/li&gt;
&lt;li&gt;Internal adversaries are often more realistic than external attackers&lt;/li&gt;
&lt;li&gt;Identity-based telemetry produces the strongest detection signals&lt;/li&gt;
&lt;li&gt;Visibility validates architecture, not just configuration&lt;/li&gt;
&lt;li&gt;Meaningful detections do not require exploitation&lt;/li&gt;
&lt;li&gt;Correlation across data sources enables true investigation&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Next Steps / Learning Opportunities
&lt;/h2&gt;

&lt;p&gt;With networking, identity, visibility, and adversarial simulation in place, future work will focus on detection quality and response workflows.&lt;br&gt;
Planned next steps include:&lt;br&gt;
Building targeted Splunk detections for reconnaissance and authentication abuse&lt;br&gt;
Mapping observed activity to MITRE ATT&amp;amp;CK techniques&lt;br&gt;
Establishing behavioral baselines&lt;br&gt;
Implementing Windows Event Forwarding (WEF)&lt;br&gt;
Expanding pfSense monitoring and alerting&lt;br&gt;
Evaluating alternative SIEM platforms such as Wazuh&lt;/p&gt;

&lt;p&gt;Why This Lab Matters&lt;/p&gt;

&lt;p&gt;This project demonstrates how networking, identity, logging, and adversarial behavior intersect in real environments. Rather than focusing on exploitation, the lab emphasizes understanding how attacks are detected, investigated, and correlated when proper visibility exists.&lt;/p&gt;

&lt;p&gt;The goal is not exploitation — it is comprehension.&lt;/p&gt;

</description>
      <category>virtualmachine</category>
      <category>networking</category>
      <category>cybersecurity</category>
      <category>homelab</category>
    </item>
    <item>
      <title>Building a Virtualized Cybersecurity Lab: Splunk SIEM Setup and Log Forwarding</title>
      <dc:creator>Evan Dolatowski</dc:creator>
      <pubDate>Sat, 13 Dec 2025 03:20:59 +0000</pubDate>
      <link>https://forem.com/gnarlylasagna/building-a-virtualized-cybersecurity-lab-splunk-siem-setup-and-log-forwarding-4k14</link>
      <guid>https://forem.com/gnarlylasagna/building-a-virtualized-cybersecurity-lab-splunk-siem-setup-and-log-forwarding-4k14</guid>
      <description>&lt;p&gt;In this third part of my virtualized cybersecurity lab series, I move beyond infrastructure and identity to focus on visibility—installing Splunk, configuring log forwarding, and building the monitoring layer required for SOC-style analysis and detection.&lt;/p&gt;

&lt;h2&gt;
  
  
  A Need For Robust Logs
&lt;/h2&gt;

&lt;p&gt;With my Active Directory environment fully deployed and all client systems joined to the domain, the next logical step in my homelab journey is establishing visibility. Identity services create the foundation of authentication and access, but without centralized logging, it’s impossible to understand how the environment behaves, detect unusual activity, or analyze the impact of simulated attacks. That’s where a SIEM comes in.&lt;/p&gt;

&lt;p&gt;In this third post of the series, I focus on installing and configuring Splunk as my primary SIEM platform, then enabling log forwarding from my Ubuntu Desktop, Windows 11, and Windows Server 2025 VM. My goal is to build a realistic monitoring stack that mirrors what security teams rely on in production environments—capturing authentication logs, system activity, and security-relevant events in one place.&lt;/p&gt;

&lt;p&gt;As with all parts of this homelab, I want a system I can continuously modify and learn from. Splunk gives me an excellent starting point for understanding enterprise log ingestion, parsing, and detection logic. In future iterations, I may migrate this VM to Wazuh or run a separate Wazuh deployment alongside Splunk so I can compare SIEM platforms and expand my defensive skill set.&lt;/p&gt;

&lt;p&gt;This post represents the beginning of the “visibility” phase of the project—turning raw system activity into actionable insights.&lt;/p&gt;

&lt;h2&gt;
  
  
  Lab Architecture Reminder
&lt;/h2&gt;

&lt;p&gt;Before diving into configuration, here’s the high-level lab design as of this stage in the project.&lt;/p&gt;

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

&lt;p&gt;Current environment includes:&lt;br&gt;
pfSense (router/firewall)&lt;br&gt;
lab-LAN internal network&lt;br&gt;
Windows Server 2022 (Domain Controller + DNS)&lt;br&gt;
Windows 11 endpoint&lt;br&gt;
Ubuntu Desktop endpoint&lt;br&gt;
With networking complete, the identity layer sits on top of this controlled environment.&lt;/p&gt;
&lt;h2&gt;
  
  
  Process Overview
&lt;/h2&gt;

&lt;p&gt;For readers who want insight into how I approached the build, I’ve included an overview of the major configuration steps I performed. Readers don't have to read every detail, this is more a summary of the important actions supported by screenshots to document the process.&lt;/p&gt;
&lt;h3&gt;
  
  
  Step 1 - Installing Splunk on Ubuntu
&lt;/h3&gt;

&lt;p&gt;Install Splunk on the VM:&lt;/p&gt;
&lt;h2&gt;
  
  
  Example for Ubuntu
&lt;/h2&gt;


&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;sudo dpkg -i splunk-9.0.x-linux-x64.deb
sudo /opt/splunk/bin/splunk start --accept-license
sudo /opt/splunk/bin/splunk enable boot-start
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;Screenshot below I am using the above commands&lt;br&gt;
&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fg0i35c9flu9nkdfh4egy.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fg0i35c9flu9nkdfh4egy.png" alt="screenshot of starting splunk server and set boot at start" width="800" height="510"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Access Splunk WebGUI:&lt;br&gt;
Go to https://&lt;em&gt;Splunk_VM_IP&lt;/em&gt;:8000&lt;br&gt;
Default admin login: admin / password you set&lt;/p&gt;

&lt;p&gt;Screenshot after logging in&lt;br&gt;
&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Finhbps3m2yxbkxpea3el.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Finhbps3m2yxbkxpea3el.png" alt="screenshot of splunk web GUI dashboard" width="800" height="643"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;
  
  
  Step 2 - Configure Inputs/Listeners in Splunk
&lt;/h3&gt;

&lt;p&gt;Universal Forwarder Input (for Windows &amp;amp; Linux agents):&lt;br&gt;
Go to Settings → Data Inputs → Forwarded Data / UDP / TCP&lt;br&gt;
Set up:&lt;br&gt;
UDP 514 (for Syslog) for Linux and pfSense&lt;br&gt;
TCP 9997 (default for Splunk forwarders)&lt;br&gt;
Enable receiving logs:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;sudo /opt/splunk/bin/splunk enable listen 9997
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Below you will see a screenshot of the ports being listened to&lt;br&gt;
&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Frnaf962ll5fflepuvb6r.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Frnaf962ll5fflepuvb6r.png" alt="screenshot proving splunk display listen ports" width="800" height="99"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Logs we plan to collect&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Security logs&lt;/li&gt;
&lt;li&gt;Sysmon operational logs&lt;/li&gt;
&lt;li&gt;Authentication events&lt;/li&gt;
&lt;li&gt;DNS logs (from DC)&lt;/li&gt;
&lt;li&gt;Linux auth logs&lt;/li&gt;
&lt;li&gt;pfSense firewall logs&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;
  
  
  Step 3 — Forward Windows Logs
&lt;/h3&gt;

&lt;p&gt;Install Splunk Universal Forwarder on Windows 11 &amp;amp; Server&lt;br&gt;
Download the Universal Forwarder from Splunk website&lt;/p&gt;

&lt;p&gt;Install on both Windows VMs&lt;br&gt;
Configure Forwarder to send logs to Splunk server:&lt;/p&gt;
&lt;h2&gt;
  
  
  Example configuration
&lt;/h2&gt;


&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;c:\Program Files\SplunkUniversalForwarder\bin\splunk add forward-server [Splunk_IP]:9997
c:\Program Files\SplunkUniversalForwarder\bin\splunk add monitor "C:\Windows\System32\winevt\Logs\Security.evtx"
c:\Program Files\SplunkUniversalForwarder\bin\splunk add monitor "C:\Windows\System32\winevt\Logs\System.evtx"
c:\Program Files\SplunkUniversalForwarder\bin\splunk start
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;Below I am listing the forward server and testing the connection to the Ubuntu Server IP&lt;br&gt;
&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fwglytslrmysv3uoj8hkp.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fwglytslrmysv3uoj8hkp.png" alt="screenshot win11 list forward server and test net connection" width="800" height="440"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;For richer telemetry, I also installed Sysmon using the SwiftOnSecurity configuration.&lt;/p&gt;
&lt;h3&gt;
  
  
  Step 4 — Forward Linux Logs
&lt;/h3&gt;

&lt;p&gt;On Ubuntu VMs:&lt;br&gt;
Install rsyslog&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;sudo apt install rsyslog
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Configure rsyslog to forward logs to Splunk:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;sudo vim /etc/rsyslog.d/60-splunk.conf
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Add:&lt;br&gt;
&lt;em&gt;.&lt;/em&gt; @@[Splunk_IP]:514&lt;br&gt;
Restart rsyslog:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;sudo systemctl restart rsyslog
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This is the content after editing the file&lt;br&gt;
&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fo8ws7bvwvnmrbua60jb7.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fo8ws7bvwvnmrbua60jb7.png" alt="screenshot command for 60-splunk file" width="747" height="349"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;
  
  
  Step 5 — Forward pfSense Logs
&lt;/h3&gt;

&lt;p&gt;Log into pfSense WebGUI&lt;br&gt;
Go to Status → System Logs → Settings&lt;br&gt;
Under Remote Syslog Servers, add the Splunk IP and UDP 514&lt;br&gt;
Select all log types we want forwarded&lt;/p&gt;

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

&lt;p&gt;pfSense sends logs in BSD-style syslog format, which Splunk parses differently than Linux logs.&lt;/p&gt;
&lt;h3&gt;
  
  
  Step 6 — Verify Logs in Splunk
&lt;/h3&gt;

&lt;p&gt;Go to Splunk Web → Search &amp;amp; Reporting&lt;br&gt;
Search for logs:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;index=* | stats count by host, sourcetype
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;we see entries from Ubuntu Desktop, Windows 11, Server 2022, and pfSense in that order&lt;br&gt;
&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fmhwzdsj2y7mrvae6bbmm.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fmhwzdsj2y7mrvae6bbmm.png" alt="screenshot of log entries from all 4 machines" width="800" height="606"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Key Takeaways / Lessons Learned
&lt;/h2&gt;

&lt;p&gt;Implementing Splunk and configuring log forwarding across every layer of the lab marked a major shift from building infrastructure to actively observing and understanding it. Once identity and authentication services were in place, centralized logging quickly became the backbone of the environment. Working through Splunk Forwarders, Sysmon, rsyslog, and pfSense log forwarding reinforced how critical consistent telemetry, accurate timestamps, and structured data inputs are for effective security monitoring. This phase made it clear that visibility is what transforms a functional network into a defensible one.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Visibility starts with identity. Once AD was deployed, it became clear how critical centralized logging is for understanding authentication patterns, user behavior, and privilege boundaries.&lt;/li&gt;
&lt;li&gt;Forwarders require careful planning. Splunk Universal Forwarders, Sysmon, and rsyslog each have unique configuration requirements, reinforcing the importance of consistency in log transport and timestamping.&lt;/li&gt;
&lt;li&gt;Telemetry sources differ widely. Windows event logs, Sysmon telemetry, Linux auth logs, and pfSense firewall logs each reveal different layers of system and network activity.&lt;/li&gt;
&lt;li&gt;Search, filtering, and verification matter. Using Splunk queries like index=* stats count by host or looking for EventCode 4624/4625 helped validate that ingestion was functioning end-to-end.&lt;/li&gt;
&lt;li&gt;This stage transforms the lab into a real defensive environment. With logs centralizing into Splunk, the environment has the foundational visibility needed for detection engineering, threat simulation, and SOC-style analysis.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Next Steps / Learning Opportunities
&lt;/h2&gt;

&lt;p&gt;With centralized logging now in place, the lab has reached a point where activity can be observed, recorded, and reviewed across every major system. Rather than immediately expanding detection logic, the next step is to introduce a realistic source of adversarial behavior so that this telemetry has meaningful context.&lt;/p&gt;

&lt;p&gt;In the next blog post, I will deploy a Kali Linux virtual machine inside the lab-LAN to act as a controlled attacking system.&lt;br&gt;
This allows attack traffic to be generated safely within the environment while maintaining full visibility across the network, identity, and logging layers. This step completes the foundational loop of the environment: networking, identity, visibility, and now an adversarial perspective to validate the logging and monitoring already in place.&lt;/p&gt;

</description>
      <category>cybersecurity</category>
      <category>splunk</category>
      <category>linux</category>
      <category>networking</category>
    </item>
    <item>
      <title>Building a Virtualized Cybersecurity Lab: Active Directory and DNS Integration</title>
      <dc:creator>Evan Dolatowski</dc:creator>
      <pubDate>Fri, 12 Dec 2025 01:34:16 +0000</pubDate>
      <link>https://forem.com/gnarlylasagna/building-a-virtualized-cybersecurity-lab-active-directory-and-dns-integration-cn6</link>
      <guid>https://forem.com/gnarlylasagna/building-a-virtualized-cybersecurity-lab-active-directory-and-dns-integration-cn6</guid>
      <description>&lt;p&gt;This is the Third blog post in a series about building my Virtual home lab network as a Cybersecurity Student.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why Identity Services Matter
&lt;/h2&gt;

&lt;p&gt;After establishing the core networking and firewall layer of my homelab, the next major step was to build the identity foundation that real-world organizations rely on: Active Directory. Windows Server 2022 provides me a fully functional domain environment with integrated DNS, centralized authentication, and the ability to manage devices across the network.&lt;/p&gt;

&lt;p&gt;This stage was especially important for me because, although I’m comfortable in Linux and the CLI thanks to my development background, I’ve always wanted to deepen my Windows administration and PowerShell skills. Studying for CompTIA A+, Network+, and Security+ made me aware of how heavily enterprises depend on Active Directory, but building it myself inside a real network pushes that knowledge much further. Completing this part of the homelab means I now have the identity infrastructure needed for future projects in security monitoring, detection engineering, and attack simulation.&lt;/p&gt;

&lt;h2&gt;
  
  
  Lab Architecture Reminder
&lt;/h2&gt;

&lt;p&gt;Before diving into configuration, here’s the high-level lab design as of this stage in the project.&lt;br&gt;
Current environment includes:&lt;br&gt;
pfSense (router/firewall)&lt;br&gt;
lab-LAN internal network&lt;br&gt;
Windows Server 2022 (Domain Controller + DNS)&lt;br&gt;
Windows 11 endpoint&lt;br&gt;
Ubuntu Desktop endpoint&lt;br&gt;
Kali Linux Attack VM (lab-LAN)&lt;br&gt;
With networking complete, the identity layer sits on top of this controlled environment.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F4enoaw2a5re41i6p56sb.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F4enoaw2a5re41i6p56sb.jpg" alt="Homelab Topology Diagram" width="800" height="800"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;h2&gt;
  
  
  Process Overview
&lt;/h2&gt;

&lt;p&gt;For readers who want insight into how I approached the build, I’ve included an overview of the major configuration steps I performed. Readers don't have to read every detail, this is more a summary of the important actions supported by screenshots to document the process.&lt;/p&gt;
&lt;h3&gt;
  
  
  Section 1 - Installing Windows Server 2022
&lt;/h3&gt;

&lt;p&gt;Windows Server 2022 required a bit more work due to Secure Boot and TPM expectations. I eventually switched from virt-install to Virt-Manager for easier overrides, but here’s the CLI version I used first:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;sudo virt-install \
  --name winserver2022 \
  --ram 8192 \
  --vcpus 4 \
  --disk path=/var/lib/libvirt/images/winserver2022.qcow2,format=qcow2,bus=virtio \
  --cdrom /var/lib/libvirt/boot/WindowsServer2022.iso \
  --network network=lab-lan,model=virtio \
  --os-variant win2k22 \
  --graphics spice \
  --boot uefi
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



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

&lt;p&gt;Make sure to attach the VirtIO driver ISO, because Windows will need these drivers to detect storage and network devices during installation.&lt;br&gt;
I also needed to create a LabConfig directory with some Booleans in order to override TPM and Secure boot.&lt;/p&gt;

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

&lt;p&gt;Assigning a Static IP (Required for AD)&lt;br&gt;
Active Directory must run on a server with:&lt;br&gt;
A static IP&lt;br&gt;
DNS pointing to itself&lt;br&gt;
A consistent hostname&lt;br&gt;
Inside Windows Server:&lt;br&gt;
Set IP: 10.0.0.25&lt;br&gt;
Subnet: 255.255.255.0&lt;br&gt;
Gateway: 10.0.0.1 (pfSense)&lt;br&gt;
DNS: 10.0.0.25 (itself)&lt;br&gt;
This is mandatory because AD DNS integrates tightly with the domain.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F2dq8hsjdxc0cr3x0mtl4.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F2dq8hsjdxc0cr3x0mtl4.png" alt="winserver iprouting" width="800" height="649"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;
  
  
  Section 2 - Promoting the Server to a Domain Controller
&lt;/h3&gt;

&lt;p&gt;Install AD DS&lt;br&gt;
In Server Manager:&lt;br&gt;
Manage → Add Roles and Features&lt;br&gt;
Select Active Directory Domain Services&lt;br&gt;
Install&lt;br&gt;
Click Promote this server to a domain controller&lt;br&gt;
Create Your Domain&lt;br&gt;
Choose Add a new forest&lt;br&gt;
Root domain: lab.local&lt;br&gt;
Set DSRM password&lt;br&gt;
Accept defaults&lt;br&gt;
Install → reboot&lt;br&gt;
After reboot, AD DS and DNS automatically come online.&lt;br&gt;
Verify AD is Working&lt;br&gt;
In PowerShell:&lt;br&gt;
If both return information about lab.local, AD is healthy.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ffq7uh13bx2sbt8v6gdok.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ffq7uh13bx2sbt8v6gdok.png" alt="getaddomain command" width="800" height="532"&gt;&lt;/a&gt;&lt;br&gt;
&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F8sd9vwwmdpehd8uel5oa.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F8sd9vwwmdpehd8uel5oa.png" alt="getadforest command" width="800" height="346"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;
  
  
  Section 3 - Configuring DNS for Internal and External Name Resolution
&lt;/h3&gt;

&lt;p&gt;AD DNS handles internal queries (lab.local), but you also need a path for external queries (google.com).&lt;br&gt;
Why DNS Forwarding Matters&lt;br&gt;
AD DNS will attempt to resolve everything—even external domains—which will fail unless forwarders are configured. This is expected behavior.&lt;br&gt;
Testing the DNS with a Ping to 8.8.8.8 fails until I configured DNS Forwarding in the pfSense Web GUI&lt;br&gt;
Fixing DNS Forwarding in pfSense&lt;br&gt;
Under Services → DNS Resolver:&lt;br&gt;
Enable Forwarding Mode&lt;br&gt;
Set upstream DNS servers (ex: 8.8.8.8)&lt;br&gt;
Save &amp;amp; apply&lt;br&gt;
This allows pfSense to resolve all external domains.&lt;br&gt;
Fixing DNS Forwarding in Windows Server&lt;br&gt;
Open DNS Manager:&lt;br&gt;
Right-click the server → Properties → Forwarders&lt;br&gt;
Add: 10.0.0.1 (pfSense LAN IP)&lt;br&gt;
This forwards unknown queries to pfSense → which forwards them to upstream DNS → out through NAT.&lt;/p&gt;

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

&lt;p&gt;Testing&lt;br&gt;
nslookup lab.local&lt;br&gt;
nslookup google.com&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;lab.local resolves internally
google.com resolves externally through pfSense
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;lab.local → resolves internally (AD DNS) ✅&lt;br&gt;
google.com → resolves externally via pfSense forwarder ✅&lt;/p&gt;
&lt;h3&gt;
  
  
  Section 4 - Joining Clients to the Domain
&lt;/h3&gt;

&lt;p&gt;A. Joining Windows 11 to the Domain&lt;br&gt;
Requirements:&lt;br&gt;
DNS pointing to 10.0.0.25 (the hard coded IP of the AD server)&lt;br&gt;
confirm Network connectivity to AD server (ping 10.0.0.25)&lt;br&gt;
System clock synchronized (Kerberos relies on accurate time)&lt;br&gt;
Join the domain:&lt;br&gt;
Settings → Accounts → Access work or school → Connect&lt;br&gt;
Choose: Join this device to a local Active Directory domain&lt;br&gt;
Enter: lab.local&lt;br&gt;
Reboot&lt;br&gt;
Verify:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;whoami
nltest /dsgetdc:lab.local
ipconfig /all
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



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

&lt;p&gt;B. Joining Ubuntu Desktop to the Domain&lt;br&gt;
Ubuntu requires packages for Kerberos + SSSD:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;sudo apt update
sudo apt install realmd sssd sssd-tools adcli samba-common-bin oddjob oddjob-mkhomedir packagekit
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Discover domain:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;realm discover lab.local
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Join:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;sudo realm join --user=administrator lab.local
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Verify:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;realm list
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Allow domain logins:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;sudo realm permit --all
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Auto-create home directories:&lt;br&gt;
Enable automatic /home creation for domain users:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;sudo pam-auth-update --enable mkhomedir
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



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

&lt;p&gt;Both Windows 11 and Ubuntu are now fully domain-joined.&lt;/p&gt;

&lt;h2&gt;
  
  
  Key Takeaways / Lessons Learned
&lt;/h2&gt;

&lt;p&gt;Building out Active Directory as the identity backbone of the lab revealed just how interconnected authentication, DNS, and domain services really are. This stage of the project required careful configuration, validation, and troubleshooting—especially around DNS and domain join workflows. Completing this step gave me a much clearer understanding of how enterprise identity environments operate and why identity services are such a critical point of failure or strength.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Active Directory and DNS are inseparable—if DNS is not functioning properly, AD will fail in unpredictable ways.&lt;/li&gt;
&lt;li&gt;Static IPs are mandatory for domain controllers and servers to ensure consistent authentication and name resolution.&lt;/li&gt;
&lt;li&gt;DNS forwarding is normal and expected in enterprise environments; AD handles internal zones while forwarding external queries.&lt;/li&gt;
&lt;li&gt;Accurate time synchronization is critical, since Kerberos authentication will break if clocks drift too far.&lt;/li&gt;
&lt;li&gt;Joining Linux systems to AD highlights how identity must work across multiple OS ecosystems.&lt;/li&gt;
&lt;li&gt;Tools like nltest, whoami, realm, and sssd provide invaluable diagnostics for verifying identity behavior.&lt;/li&gt;
&lt;li&gt;This phase showed that identity becomes the central dependency for every layer that follows—logging, access control, monitoring, and detection.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Next Steps / Learning Opportunities
&lt;/h2&gt;

&lt;p&gt;With the identity layer fully established and all clients joined to the domain, the next phase of the project shifts toward visibility and event analysis. Now that authentication and DNS are stable, I can begin focusing on the logs, behaviors, and telemetry that will move this lab from a simple AD deployment to a functional security operations environment.&lt;/p&gt;

&lt;p&gt;In the next stage of the homelab build, I’ll begin implementing a full monitoring and detection pipeline. This will include:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Creating Organizational Units (OUs) and applying Group Policies (GPOs)&lt;/li&gt;
&lt;li&gt;Installing and configuring Sysmon for advanced process and network telemetry&lt;/li&gt;
&lt;li&gt;Setting up Windows Event Forwarding (WEF) to centralize Windows logs&lt;/li&gt;
&lt;li&gt;Deploying Splunk Universal Forwarders across server and endpoint systems&lt;/li&gt;
&lt;li&gt;Aggregating and parsing logs inside the Splunk SIEM&lt;/li&gt;
&lt;li&gt;Running simulated attacks from my Kali machine to test identity and authentication monitoring&lt;/li&gt;
&lt;li&gt;Beginning SOC-style analysis, including search queries, baselining, and detection logic development&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;With networking, segmentation, and identity now in place, the environment is ready to evolve into a true security engineering and blue-team practice lab. The next blog post will document the start of this visibility phase as I install Splunk and configure log forwarding across the environment.&lt;/p&gt;

</description>
      <category>homelab</category>
      <category>dns</category>
      <category>virtualization</category>
      <category>sysadmin</category>
    </item>
    <item>
      <title>Building a Virtualized Cybersecurity Lab: Networking and pfSense Setup</title>
      <dc:creator>Evan Dolatowski</dc:creator>
      <pubDate>Fri, 12 Dec 2025 01:33:13 +0000</pubDate>
      <link>https://forem.com/gnarlylasagna/building-a-virtualized-cybersecurity-lab-networking-and-pfsense-setup-4bml</link>
      <guid>https://forem.com/gnarlylasagna/building-a-virtualized-cybersecurity-lab-networking-and-pfsense-setup-4bml</guid>
      <description>&lt;p&gt;This is the First of Four blog posts in a series about building my Virtual home lab network as a Cybersecurity Student.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Reason Why
&lt;/h2&gt;

&lt;p&gt;Over the past year, I’ve used individual virtual machines, Docker containers, and online training platforms like TryHackMe to build hands-on cybersecurity experience. Although these tools are valuable, they don’t provide the realism or complexity of working inside a fully controlled network environment. With the start of my Cyber Security degree at Maryville University—and after moving my main workstation to a Fedora-based Linux distribution when Windows 10 reached end of life—I decided it was the right time to build a proper virtualized homelab. Running Linux as my host OS also let me avoid Windows 11’s hardware requirements such as Secure Boot and TPM, giving me full control over my virtualization stack and allowing me to design an enterprise-style lab tailored specifically for cybersecurity training.&lt;/p&gt;

&lt;p&gt;This post is the first entry in a four-part series documenting my homelab build. Here, I cover the foundational architecture choices, explain how I configured the virtualization environment, and walk through the process of deploying pfSense as the core router and firewall for the entire network. Because my workstation runs Linux natively, I chose KVM and QEMU for their performance, flexibility, and deep integration with the OS. Combined with libvirt, virsh, and virt-viewer, this gives me a fast, lightweight, and highly customizable virtualization ecosystem—ideal for learning how real infrastructure operates behind the scenes.&lt;/p&gt;

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

&lt;h2&gt;
  
  
  My Lab Is Organized Around Four Primary Components:
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;Virtual Networks
lab-NAT – Provides pfSense with upstream internet access through the Linux host.
lab-LAN – A fully isolated internal network where all client and server VMs live.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;This separation models a real enterprise network with distinct external and internal segments.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;pfSense Firewall/Router
pfSense serves as the central security appliance of the lab, providing:&lt;/li&gt;
&lt;li&gt;Routing between WAN and LAN&lt;/li&gt;
&lt;li&gt;Stateful firewalling&lt;/li&gt;
&lt;li&gt;DHCP services&lt;/li&gt;
&lt;li&gt;NAT translation&lt;/li&gt;
&lt;li&gt;Segmentation and network boundary control&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;All traffic entering or leaving the internal network passes through pfSense, mirroring the design of most corporate environments.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Internal Systems
These are the systems that make up the “enterprise network” inside the lab:
Windows Server 2022 (Active Directory + DNS)
Windows 11 workstation
Ubuntu Desktop running Splunk
Additional Linux servers for testing and future projects&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;This provides a functional identity, endpoint, and logging ecosystem that represents a realistic enterprise environment.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Kali Linux Attack VM (lab-LAN)
A Kali Linux virtual machine deployed inside the lab-LAN to simulate adversarial behavior from a controlled internal or semi-trusted network position.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;This setup allows me to perform penetration testing, vulnerability discovery, and attack simulations against the isolated lab-LAN—while keeping everything safely contained and segmented from my real devices.&lt;/p&gt;

&lt;h2&gt;
  
  
  Process Overview
&lt;/h2&gt;

&lt;p&gt;For readers who want insight into how I approached the build, I’ve included an overview of the major configuration steps I performed. Readers don't have to read every detail, this is more a summary of the important actions supported by screenshots to document the process.&lt;/p&gt;

&lt;h3&gt;
  
  
  Section 1 — Preparing the Virtualization Stack
&lt;/h3&gt;

&lt;p&gt;Installing KVM/QEMU and Tools&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;sudo rpm-ostree install @virtualization virt-manager libvirt libvirt-daemon-kvm qemu-kvm bridge-utils --allow-inactive
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;What this stack provides:&lt;/p&gt;

&lt;p&gt;KVM/QEMU: hardware-accelerated virtualization&lt;br&gt;
libvirt: management layer for VMs&lt;br&gt;
virsh: CLI tool for creating and controlling VMs&lt;br&gt;
virt-manager / virt-viewer: optional GUI tools&lt;/p&gt;

&lt;p&gt;Enable and Start libvirt&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;sudo systemctl enable --now libvirtd
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Verify:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;sudo systemctl status libvirtd
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Verify KVM Support&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;egrep -c '(vmx|svm)' /proc/cpuinfo
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Section 2 — Creating the Virtual Networks
&lt;/h3&gt;

&lt;p&gt;My homelab will use two networks:&lt;br&gt;
lab-NAT (WAN side)&lt;br&gt;
Connects pfSense to the internet&lt;br&gt;
Uses NAT from the Linux host&lt;br&gt;
lab-LAN (internal network)&lt;br&gt;
Hosts all internal machines&lt;br&gt;
Completely isolated from the host&lt;br&gt;
pfSense handles all routing and DHCP&lt;/p&gt;

&lt;p&gt;Example XML for lab-LAN and lab-NAT:&lt;/p&gt;

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

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

&lt;p&gt;Define and start the lab-lan after creating its XML file:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;sudo virsh net-define /etc/libvirt/qemu/networks/lab-lan.xml
sudo virsh net-start lab-lan
sudo virsh net-autostart lab-lan
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;this process is mostly the same for creating the lab-nat&lt;/p&gt;

&lt;p&gt;Verify by listing all networks running:&lt;/p&gt;

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

&lt;h3&gt;
  
  
  Section 3 — Deploying pfSense (The Core of the Lab)
&lt;/h3&gt;

&lt;p&gt;pfSense is the heart of the homelab and acts as:&lt;br&gt;
Router&lt;br&gt;
Firewall&lt;br&gt;
DHCP server&lt;br&gt;
Gateway for the lab-lan virtual network&lt;br&gt;
Segmentation control&lt;br&gt;
This mirrors what most corporate networks use.&lt;/p&gt;

&lt;p&gt;Creating the pfSense VM with Virt Install&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;sudo virt-install \
  --name pfSense \
  --ram 2048 \
  --vcpus 2 \
  --os-variant freebsd13.0 \
  --disk path=/var/lib/libvirt/images/pfSense.qcow2,size=10,format=qcow2 \
  --cdrom /var/lib/libvirt/boot/pfsense.iso \
  --network network=lab-nat,model=virtio \
  --network network=lab-lan,model=virtio \
  --graphics spice \
  --boot uefi
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Open installer:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;sudo virt-viewer pfSense
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;pfSense Interface Assignment&lt;br&gt;
pfSense will detect and ask you to confirm the LAN and WAN:&lt;br&gt;
vtnet0 → WAN (lab-NAT)&lt;br&gt;
vtnet1 → LAN (lab-LAN)&lt;br&gt;
Set the LAN IP&lt;br&gt;
I assigned a memorable static IP to the pfSense LAN interface for easier VM configuration and future reference:&lt;br&gt;
Class A starting with 10.0.0.1&lt;br&gt;
and a subnet mask of 255.255.255.0 gives me plenty of room for my small network&lt;br&gt;
LAN IP: 10.0.0.1/24&lt;br&gt;
DHCP range: 10.0.0.50–10.0.0.200&lt;br&gt;
After this point, pfSense becomes the router and firewall.&lt;br&gt;
Every VM ill attach to lab-lan receives:&lt;br&gt;
IP: 10.0.0.x&lt;br&gt;
Gateway: 10.0.0.1&lt;br&gt;
DNS: 10.0.0.1&lt;br&gt;
Internet routed through pfSense&lt;/p&gt;

&lt;p&gt;This completes the initial network segmentation and routing.&lt;/p&gt;

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

&lt;h2&gt;
  
  
  Key Takeaways / Lessons Learned
&lt;/h2&gt;

&lt;p&gt;Building the firewall and virtual networking foundation for my homelab significantly improved my confidence with virsh, virt-viewer, and KVM-based virtualization. This phase reinforced key infrastructure concepts such as defining virtual networks, bridging interfaces, configuring routing, and assigning DHCP at the firewall layer. Deploying pfSense helped deepen my understanding of segmentation, NAT behavior, and multi-interface design. Altogether, these steps gave me hands-on experience creating a network architecture that mirrors what real-world IT and security teams use in production.&lt;/p&gt;

&lt;h2&gt;
  
  
  Next Steps / Learning Opportunities
&lt;/h2&gt;

&lt;p&gt;With the core networking and firewall components complete, my next goal is to expand pfSense’s capabilities and shift toward a more security-focused environment. I plan to explore advanced firewall rules, VLAN segmentation, DNS resolver tuning, and static DHCP mappings for key hosts. I also want to evaluate pfSense packages including pfBlockerNG for threat intelligence and Suricata for IDS/IPS functionality, adding deeper traffic inspection and detection capability.&lt;/p&gt;

&lt;p&gt;In the next post, I’ll move into the identity layer by installing Windows Server 2022, promoting it to a Domain Controller, configuring DNS, and integrating it with pfSense. I will join both Windows and Linux clients to the domain and verify authentication across the environment. This will establish the Active Directory foundation that future posts will build on—covering SOC workflows, blue-team tooling, detection engineering, and attack simulations.&lt;/p&gt;

</description>
      <category>homelab</category>
      <category>pfsense</category>
      <category>networking</category>
      <category>virtualization</category>
    </item>
    <item>
      <title>Cloud Resume Challenge</title>
      <dc:creator>Evan Dolatowski</dc:creator>
      <pubDate>Tue, 23 Apr 2024 00:45:03 +0000</pubDate>
      <link>https://forem.com/gnarlylasagna/cloud-resume-challenge-2jik</link>
      <guid>https://forem.com/gnarlylasagna/cloud-resume-challenge-2jik</guid>
      <description>&lt;h1&gt;
  
  
  Transitioning to the Cloud: My Journey with Azure
&lt;/h1&gt;

&lt;p&gt;Hey everyone! My name is Evan Dolatowski from Houston, Texas, and I’m a Full Stack developer. I'm currently working towards transitioning to the Cloud industry. The realization of an important gap in my knowledge and the adventure of educating myself more deeply about Cloud technologies began when I ended up hosting my full stack portfolio project using Netlify, Heroku, and Amazon S3 buckets. I didn’t expect to use Amazon S3 Buckets, but it was needed to solve an issue Heroku has with persistent file storage caused by the free hosting. As soon as I realized I needed this third hosting account for my application, I knew my use of the cloud was far from optimized. I needed to learn how to use one of the three large cloud providers: AWS, GCP, or Azure, and I wanted to make sure I knew how to use it well.&lt;/p&gt;

&lt;p&gt;After researching jobs in my local area, I found Azure jobs are by far more common than AWS or GCP. So I knew my Cloud platform, familiarized myself with the Certifications available and got the AZ-900 Azure Fundamentals. My next goal was the AZ-104. Once I felt confident in the material and almost ready to take the Exam, I wanted to get some real hands-on experience using Azure services for a difficult project. This is where I discovered The Cloud Resume Challenge.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Objectives
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;The Az-900 Azure Fundamentals certification was a prerequisite to the challenge (luckily I already had it).&lt;/li&gt;
&lt;li&gt;Your Resume needs to written in HTML, styled with CSS, and hosted on a Azure Storage static website.&lt;/li&gt;
&lt;li&gt;The Azure Storage website URL should use HTTPS for security Using Azure CDN and Point a custom DNS domain name to the Azure CDN endpoint.&lt;/li&gt;
&lt;li&gt;On the website there needs to be a visitor counter using Javascript request to a Database containing the count.&lt;/li&gt;
&lt;li&gt;This Database would be Azure Cosmos DB account.&lt;/li&gt;
&lt;li&gt;The Front End should not speak directly to the DB, an API written and tested in Python will accept the request and send SQL queries to the Database. (Azure Functions with an HTTP trigger).&lt;/li&gt;
&lt;li&gt;The backend portion of the project should be defined using an ARM template on a Consumption plan and not manually in the Azure Portal.&lt;/li&gt;
&lt;li&gt;The Git Repo needs to use CI/CD for both front and back end set up through Github actions for quick automated deployment.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;And the final step, write a short blog post describing some things you learned while working on this project.&lt;/p&gt;

&lt;h2&gt;
  
  
  Frontend
&lt;/h2&gt;

&lt;p&gt;Creating the front end and deploying it to the Azure Storage static website wasn’t nearly as difficult as getting my custom domain set up. Setting up a CDN Profile, DNS Zone, and Purchasing a Custom Domain were all very new tasks for me. A lot of trial and error went into this, but probably more trial and error than I needed. I think if I had updated the DNS settings and patiently waited before I tested it would have worked out for me, but in the end, I probably deleted and recreated all my frontend resources in my resource group over a dozen times, sometimes with the Azure CLI and sometimes with the Portal. I spent a lot of time interacting with the Azure CLI and portal which was the reason I wanted to do the challenge so I'm kind of glad it didn’t just work right away.&lt;/p&gt;

&lt;h2&gt;
  
  
  DB/Serverless Python API
&lt;/h2&gt;

&lt;p&gt;Next, I needed to make the DB and the Azure Serverless function using Python. This is something silly but I think Azure Cosmos DB is so cool. It’s so quick to create a SQL DB with Azure and quickly connect a Python script to it using The Azure Cosmos DB SQL API library and start using queries. I was excited the challenge already recommends using Serverless functions because its cost-effective for a small project as well as a relatively new technology because I had already wanted to learn more about Serverless Functions. Initializing, creating the template, and testing this function was easy enough thanks to the Azure-CLI. Big thanks to the func init, func new and func start commands.&lt;/p&gt;

&lt;h2&gt;
  
  
  CI/CD
&lt;/h2&gt;

&lt;p&gt;Finally, we have CI/CD. I love the end result of this step. I think it’s literally so cool that 1 minute after I type ‘git push’ in the terminal the new update is deployed to my custom URL. (If my code passes the tests of course). I've used CI/CD before but I wanted to get more practice with it so I was excited it's included in the challenge. A ton of trial and error went into my frontend YAML file dealing with Node versions and dependencies, as well as my code runs a build script and the build is put into a directory called ‘src’ this src directory contains the files that will then be served in my Azure Storage static site. The backend was much less of a headache but both were a much-welcomed challenge. It’s nice to have the slow progress tackle one small problem at a time until it finally deploys with no errors.&lt;/p&gt;

&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;I feel great about using Azure. I spent a lot of time using the Azure CLI and Portal to create update and delete many kinds of resources while doing this challenge which was a very important part of this challenge to me. I'm excited to spend a bit more time studying and get my second Certification, the AZ-104.&lt;/p&gt;

&lt;p&gt;Thank you Forrest Brazeal for creating this challenge, and thank you for creating the opportunity to meet the other takers of this challenge and network with like-minded individuals.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://www.evandolatowski.com/" rel="noopener noreferrer"&gt;EvanDolatowski.com&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://github.com/GnarlyLasagna/portfolio-site" rel="noopener noreferrer"&gt;Link to frontend code&lt;/a&gt;&lt;br&gt;
&lt;a href="https://github.com/GnarlyLasagna/portfolio-server-less-py" rel="noopener noreferrer"&gt;Link to backend code&lt;/a&gt;&lt;br&gt;
Feel free to reach out on &lt;a href="https://www.linkedin.com/in/evan-dolatowski-a928aa21b/" rel="noopener noreferrer"&gt;LinkedIn&lt;/a&gt; if you’ve got any questions.&lt;/p&gt;

</description>
      <category>cicd</category>
      <category>githubactions</category>
      <category>azure</category>
      <category>serverless</category>
    </item>
  </channel>
</rss>
