[{"content":"","date":"8 January 2026","externalUrl":null,"permalink":"/","section":"Blog","summary":"","title":"Blog","type":"page"},{"content":" THE LAB # Today we will be building a minimal SOC lab to practice our network defense, endpoint monitoring, and incident response skills.\nWe will construct the Lab using Virtual Machines, and since I am on linux I will be using using Qemu as my virtualization software but you can use anything else.\nLab Structure # Our lab will have different components (VMs), these are the components that we will be using,\npfSense (Firewall \u0026amp; Router)\nSuricata IDS (pfSense Add-on)\nWazuh SIEM (Hosted on Ubuntu)\nWindows Endpoint\nKali\nWe will be setting up each of these VMs and configure for their communication.\ninfo 32GB of RAM is recommended to run all of these VMs together.\nConfiguring Virtual Machines # We are going to build and configure each VM step-by-step. Follow along, and you should be fine. If you encounter any problems, you are basically on your own but quick internet search will most likely solve your issues.\npfSense VM (Firewall \u0026amp; Router) # I assume you have already downloaded the ISO, but if you haven\u0026rsquo;t go ahead and download it from here. Now, lets create the VM using Qemu (Virt-Manager).\nCreation # Select your pfSense ISO Select Generic or unknown OS. Set Memory: 2048 MiB and vCPUs: 2 Storage: 20 GiB Click the \u0026ldquo;Customize configuration before install\u0026rdquo; checkbox Finish Configuration # We need to add a second Network Interface Card (NIC). In the setting tab, click Add Hardware -\u0026gt; Network, here select Isolated Network. It should look similar to this,\nNow, click the Begin installation button at top right, and boot the machine.\nWhen prompted, choose Install pfSense. Then follow these steps,\nSelect Auto (UFS) for disk partitioning. Select Entire Disk Select MBR DOS Partitions If everything is correct your partitioning scheme should look like this,\nSelect Finish and continue, it will install automatically. After it finishes with installation, reboot the VM.\nYou should see a screen like this after the reboot, listing your NICs with as WAN (em0) and LAN (em1). We will setup LAN interface for our internal lab network.\nFollow these steps:\nPress 2 -\u0026gt; Set interface IP address Select em1 (LAN). Press n -\u0026gt; Configure IPv4 address LAN interface via DHCP Enter new IPv4 address -\u0026gt; 10.10.10.1 (you can change and use your own IP range) Enter subnet mask -\u0026gt; 24 For upstream gateway, leave blank (LAN doesn’t need it) Press n -\u0026gt; Configure IPv6 address LAN interface via DHCP6 Press Enter for none, we don\u0026rsquo;t need IPv6 Press y -\u0026gt; Enable DHCP server for LAN (this will give your VMs IPs automatically.) Enter start address -\u0026gt; 10.10.10.2 (you can change this) Enter end address -\u0026gt; 10.10.10.100 (you can change this) Press y -\u0026gt; Do you want to revert to HTTP as the webConfigurator protocol If everything is correct, it should look like this,\nnow you can use the web configuration page by visiting, http://10.10.10.1 on a VM attached to Isolated Network.\nWazuh SIEM # We will be using Ubuntu to host our Wazuh instance, lets create the Ubuntu VM.\nCreation # Select your Ubuntu ISO Set Memory: 8192 MiB and vCPUs: 4 Storage: 40 GiB Click the \u0026ldquo;Customize configuration before install\u0026rdquo; checkbox Finish Configuration # Before the installation, change the NIC -\u0026gt; Network Source to Isolated Network. Then, begin installation. I\u0026rsquo;ve chosen minimal installation but you can choose whatever you like.\nAfter the installation finishes, you can check the IP address of the VM and confirm that it got this IP address from the pfSense DHCP.\npfSense VM should already be running for Ubuntu VM to get the IP address.\nIt should look like this,\nyou will notice that you cannot connect to the internet because the VM\u0026rsquo;s DNS is not configured yet.\nTo do this, use NetworkManager:\n# Find connection name nmcli connection show # Set DNS sudo nmcli connection modify \u0026#34;Wired connection 1\u0026#34; \\ ipv4.ignore-auto-dns yes \\ ipv4.dns \u0026#34;8.8.8.8 8.8.4.4\u0026#34; # Restart connection sudo nmcli connection down \u0026#34;Wired connection 1\u0026#34; sudo nmcli connection up \u0026#34;Wired connection 1\u0026#34; You should see your internet connection and have no issues.\nNow, that we have fixed the issue and confirmed that our pfSense supplies IP addresses correctly we can continue with the Wazhuh installation.\nFollow these steps:\n# 1. Update the system sudo apt update \u0026amp;\u0026amp; sudo apt upgrade -y # 2. Set hostname (optional) hostnamectl set-hostname wazuh-manager # 3. Download the Wazuh Install Script and Run it curl -sO https://packages.wazuh.com/4.14/wazuh-install.sh \u0026amp;\u0026amp; sudo bash ./wazuh-install.sh -a # 4. Verify Services sudo systemctl status wazuh-manager sudo systemctl status wazuh-indexer sudo systemctl status wazuh-dashboard After the installation, the script will prompt you to a password which you’ll need it to log into the dashboard. You can login to dashboard by visiting http://localhost.\nOur SIEM is ready now, we will be adding an Wazuh agent which will be our Windows endpoint. Wazuh agent will forward Windows logs to our Wazuh Manager.\nWindows Endpoint # We will be creating the Windows VM now, which will be our Wazuh agent.\nCreation # Select your Windows ISO Set Memory: 4096 MiB and vCPUs: 4 Storage: 40 GiB Click the \u0026ldquo;Customize configuration before install\u0026rdquo; checkbox Finish Configuration # Again like we did on the Ubuntu VM, before the installation, change the NIC -\u0026gt; Network Source to Isolated Network. Then, begin installation.\nYou need to setup DNS inside the VM, you can easily change it using the adapter options.\nTIP -\u0026gt; you can change the windows vm name to wazuh-agent.\nFor our Windows endpoint setup, we are going to install Sysmon for detailed log analysis.\nFollow these steps:\nInstall Sysmon Download Sysmon from Microsoft Sysinternals Download a basic sysmon configuration file using PowerShell (start as admin) Install Sysmon with the configuration Verify installation # Download config Invoke-WebRequest -Uri https://raw.githubusercontent.com/SwiftOnSecurity/sysmon-config/master/sysmonconfig-export.xml -OutFile C:\\sysmonconfig.xml # Install sysmon with config .\\Sysmon64.exe -accepteula -i C:\\sysmonconfig.xml # Verify installation sysmon64.exe -c The output should look like this after the verification,\nInstall Wazuh agent Download the agent msi file from here Start PowerShell as admin, and nagivate to your downloads folder Deploy the Wazuh agent Start the Wazuh agent service # Deploy the agent .\\wazuh-agent-4.14.1-1.msi /q WAZUH_MANAGER=\u0026#34;10.10.10.2\u0026#34; # your manager ip # Start the agent service Start-Service wazuhsvc Test the connection from Wazuh dashboard, you should see 1 active agent like this,\nNow we need to edit the agent configuration file to also include the logs from Sysmon and PowerShell.\nStart PowerShell as admin, and navigate to C:\\Program Files (x86)\\ossec-agent and edit the ossec.conf file.\ncd C:\\Program Files (x86)\\ossec-agent notepad ossec.conf Add these 2 config lines after already existing default ones.\n\u0026lt;localfile\u0026gt; \u0026lt;location\u0026gt;Microsoft-Windows-Sysmon/Operational\u0026lt;/location\u0026gt; \u0026lt;log_format\u0026gt;eventchannel\u0026lt;/log_format\u0026gt; \u0026lt;/localfile\u0026gt; \u0026lt;localfile\u0026gt; \u0026lt;location\u0026gt;Microsoft-Windows-Powershell/Operational\u0026lt;/location\u0026gt; \u0026lt;log_format\u0026gt;eventchannel\u0026lt;/log_format\u0026gt; \u0026lt;/localfile\u0026gt; Restart the agent with Restart-Service wazuhsvc, and check on the Wazuh dashboard and verify that Sysmon, and PowerShell logs are being forwarded.\nWhen you expand the related event you should see provider similar to this,\nAfter you verify that logs are being forwarded as expected, we can continue with the Suricata (IDS) setup.\nSuricata (IDS) # Remember we have configured pfSense before but never visit the pfSense dashboard. We will configure Suricata using this dashboard.\nSince it is our first time using the dashboard, pfSense will launch a setup wizard.\nFollow these steps:\nGeneral Information Hostname: e.g., pfsense-lab Domain: e.g., lab.local DNS servers: you can leave empty (it will use WAN default) Time Zone Select your lab’s timezone WAN Interface Should already be configured for DHCP (your WAN is using NAT to your host network) Leave it as is LAN Interface Should match the LAN IP you set earlier (e.g., 10.10.10.1/24) DHCP should already be enabled with the range you configured Admin Password Set a new password for the admin account Finish pfSense will apply settings and reload the web GUI Now let\u0026rsquo;s install the Suricata add-on. Go to System -\u0026gt; Package Manager -\u0026gt; Available Packages, search for Suricata and install it.\nVerify that Suricata is installed properly:\nConfigure Suricata # Go to Services -\u0026gt; Suricata, add Interface in this case LAN (em1).\nThen, edit LAN interface under Actions tab:\nWe need to enable EVE json logs to forward logs to Wazuh manager.\nMake sure that these 2 options are checked:\nEVE JSON log -\u0026gt; enabled\nEVE Output type -\u0026gt; FILE\nOther output settings are optional, enable them according to your needs.\nI\u0026rsquo;ve also enabled ETOpen rule set under Global Settings (this is optional). After this, go to Updates tab, and update rules.\nRestart the Suricata service for changes to take effect.\nThere are 2 main ways to forward logs from pfSense/Suricata to Wazuh manager. Using either rsyslog or the wazuh-agent. I\u0026rsquo;ve setup rsyslog before but I found it unstable and actually harder to troubleshoot therefore I will be demonstrating the second option.\nInstall wazuh-agent on pfSense # We are going to edit some configuration files to be able to use and install FreeBSD packages. You can edit these files by using the built-in vi editor, but I will be using the pfSense dashboard, Diagnostics -\u0026gt; Edit File option for easiness.\nLoad and edit file, /usr/local/etc/pkg/repos/pfSense.conf and save it.\nLoad and edit file, /usr/local/etc/pkg/repos/FreeBSD.conf, and save it.\nNow that we enabled FreeBSD packages, we can download the wazuh-agent. You can use the shell from the pfSense by pressing 8 or use the pfSense dashboard again, go to Diagnostics -\u0026gt; Command Prompt.\nUse these commands to install:\n# Update the package cache pkg update # Search the package cache for the official agent pkg search wazuh-agent # Install the agent # Replace x.xx.x with your version number pkg install wazuh-agent-x.xx.x Configure and enable the wazuh-agent # We need to edit the agent configuration file. Use the pfSense dashboard again to edit the file, /var/ossec/etc/ossec.conf.\nAdd your Wazuh manager IP address, and make sure that you use the correct protocol. I am using TCP here, but if you are not sure check this from Wazuh manager\u0026rsquo;s ossec.conf file.\nTo automatically start Wazuh agent at pfSense boot, follow these commands:\nsysrc -f /etc/rc.conf.local wazuh_agent_enable=\u0026#34;YES\u0026#34; ln -s /usr/local/etc/rc.d/wazuh-agent /usr/local/etc/rc.d/wazuh-agent.sh service wazuh-agent start After setting up Suricata and the Wazuh agent, we need to add the configuration for Wazuh manager to start analyzing FW and Suricata logs.\nYou can directly edit the /var/ossec/etc/ossec.conf for pfSense agent or as I will do, you can add a new group on the Wazuh manager and add this config for all of the agents under that group.\nGo to Agents Management -\u0026gt; Groups and Add new group. Once you create it, click on the group. You will see that its empty. To add an agent for this group, click Manage agents and add your pfSense agent from the listing.\nAfter you add the agent, go back a step and now edit the group configuration.\nAdd these inside the \u0026lt;agent_config\u0026gt;, once saved the Wazuh manager will apply the changes for every agent under this group.\n\u0026lt;localfile\u0026gt; \u0026lt;log_format\u0026gt;json\u0026lt;/log_format\u0026gt; \u0026lt;location\u0026gt;/var/log/suricata/*/eve.json\u0026lt;/location\u0026gt; \u0026lt;/localfile\u0026gt; \u0026lt;localfile\u0026gt; \u0026lt;log_format\u0026gt;syslog\u0026lt;/log_format\u0026gt; \u0026lt;location\u0026gt;/var/log/filter.log\u0026lt;/location\u0026gt; \u0026lt;/localfile\u0026gt; Should look like this,\nWe are done with the setup. Now, let\u0026rsquo;s generate some events and analyze it on the Wazuh dashboard.\nSimulating Attacks and Analyzing Events # For this section, I will be demonstrating 2 scenarios.\nCompromised Windows Endpoint and running tmNIDS Compromised Isolated Network and running nmap scans to Windows endpoint from attacker machine (Kali). tmNIDS framework # We can create false positive events and test functionality using tmNIDS framework. This is an interactive script that simulates the interaction with 3rd party websites, and meant to be run on the endpoint. We can technically run this script on the Wazuh manager (Ubuntu), but we are going to run it on the Windows endpoint.\ninfo This script is designed primarily for linux endpoints, therefore you cannot directly run it on PowerShell. Install, git bash or similar to run this bash script on your Windows endpoint.\nRun this command in git bash:\ncurl -sSL https://raw.githubusercontent.com/3CORESec/testmynids.org/master/tmNIDS -o tmNIDS \u0026amp;\u0026amp; chmod +x tmNIDS \u0026amp;\u0026amp; ./tmNIDS You will see the interactive mode, here you can run any tests that you want. I ran the External IP Address Lookup. Let\u0026rsquo;s look at the generated events.\nI added the filter data.flow.src_ip for my Windows endpoint IP address:\nChecking the events, we can see that events have been generated for the External IP Address Lookup test.\nWhen we expand the event we can observe interesting fields like, data.flow.dest_ip, data.http.hostname, data.http.http_user_agent, rule.description, etc. Event details clearly explain the test that we have run, also we can see the Suricata alert and which rule that the test hit.\nKali # Create the Kali VM, you can create it from the ISO or use the pre-built VM. I am using the pre-built one for this guide.\nVM Specs:\nMemory: 2048 MiB and vCPUs: 2 Storage: 80 GiB NIC: Isolated network In this case attacker has compromised the isolated network, and can run nmap port scans across the compromised network. This is a bit tricky to investigate in our events because attacker machine and Windows endpoint are on the same L2 network, so traffic never hits pfSense at all.\nTherefore, this SOC lab to actually detect internal port scanning, we need to enable firewall logging on the Windows endpoint.\nDo this by following these steps:\nOpen Windows Defender Firewall with Advanced Security. Click Properties For each profile tab (Domain, Private, Public) customize logging Name: %systemroot%\\system32\\LogFiles\\Firewall\\pfirewall.log Size: 16,384 Log dropped packets: Yes Log successful connections: Yes Click OK Enable Security Auditing for Firewall Open Local Security Policy Go to Advanced Audit Policy Configuration -\u0026gt; System Audit Policies -\u0026gt; Object Access Enable: Audit Filtering Platform Connection: Success + Failure Audit Filtering Platform Packet Drop → Success + Failure Apply and close As the last step, we need to add this log file to the Wazuh agent configuration file to be ingested by the Wazuh manager.\nOpen PowerShell as admin, and edit the configuration file with this command:\nnotepad \u0026#39;C:\\Program Files (x86)\\ossec-agent\\ossec.conf\u0026#39; At the end of the other \u0026lt;localfile\u0026gt; tags, add this:\n\u0026lt;localfile\u0026gt; \u0026lt;location\u0026gt;C:\\Windows\\System32\\LogFiles\\Firewall\\pfirewall.log\u0026lt;/location\u0026gt; \u0026lt;log_format\u0026gt;syslog\u0026lt;/log_format\u0026gt; \u0026lt;/localfile\u0026gt; and restart the Wazuh agent service, Restart-Service wazuhsvc.\nOn Wazuh manager, we need to enable logall and logall_json options in the configuration file, but be aware that this will increase the log size.\nEdit /var/ossec/etc/ossec.conf and set both option to yes.\nWazuh manager does not index archives by default, so we need to enable it in the filebeat.yml file.\nEdit /etc/filebeat/filebeat.yml and set archives: enabled option to true.\nRestart filebeat service, sudo systemctl restart filebeat.\nWe enabled raw logging and enabled archives in filebeat. We should add the wazuh-archives-* index.\nVerify that wazuh-archives exists, go to Index Management -\u0026gt; Indexes, you should see an archive named as wazuh-archive-****.\nTo add this archive as an index, go to Dashboards Management -\u0026gt; Index patterns. Create index pattern, name it wazuh-archives-* and select the time field as @timestamp. After you create the index, we can now select this newly created index and check events under Explore -\u0026gt; Discover tab.\nWe can finally start an nmap port scan from kali:\nnmap -sS -p- -T4 -Pn 10.10.10.14 # Windows IP We should be able to see the firewall logs on the Windows endpoint and see which connections are allowed or dropped.\nAfter running the scan, we can see from the events that incoming connection from IP 10.10.10.15 (kali) is DROPPED. We can see the full log line and the rule description.\nAdditionally, we can create more noise on Windows endpoint by running some tests from Atomic Red Team and see incoming logs from Event Viewer and Sysmon. However, I will not demonstrate since we have already verified logs from these resources are indeed ingested by the Wazuh manager.\nUseful Reads # https://documentation.wazuh.com/current/index.html https://opennix.org/en/docs/pfsense/pfsense-wazuh-integration/ https://benheater.com/integrating-pfsense-with-wazuh/ ","date":"8 January 2026","externalUrl":null,"permalink":"/posts/building-a-mini-soc-lab-at-home/","section":"Posts","summary":"","title":"Building a Mini SOC Lab at Home","type":"posts"},{"content":"","date":"8 January 2026","externalUrl":null,"permalink":"/tags/cysec/","section":"Tags","summary":"","title":"Cysec","type":"tags"},{"content":"","date":"8 January 2026","externalUrl":null,"permalink":"/tags/lab/","section":"Tags","summary":"","title":"Lab","type":"tags"},{"content":"","date":"8 January 2026","externalUrl":null,"permalink":"/posts/","section":"Posts","summary":"","title":"Posts","type":"posts"},{"content":"","date":"8 January 2026","externalUrl":null,"permalink":"/tags/soc/","section":"Tags","summary":"","title":"SOC","type":"tags"},{"content":"","date":"8 January 2026","externalUrl":null,"permalink":"/tags/","section":"Tags","summary":"","title":"Tags","type":"tags"},{"content":"","date":"3 January 2026","externalUrl":null,"permalink":"/tags/blog/","section":"Tags","summary":"","title":"Blog","type":"tags"},{"content":"On this post, I will be demonstrating quick and easy way of creating static websites from your obsidian notes. This is possible with Hugo which is an open-source framework for static site generation.\ninfo Hugo basically turns markdown files to html, so you can still follow along even if you don\u0026rsquo;t use obsidian and skip the obsidian related parts.\nSince I am on arch, some commands may not work for you. If that is the case refer the original documentation.\nPrerequisites for Hugo:\nInstall Git Install Go Install Hugo:\nsudo pacman -S hugo Create new site using Hugo (this creates the site under a folder):\nhugo new site blog Initialize git repo:\ncd blog/ git init Download a theme (I am using Blowfish, this is optional but recommended:\ngit submodule add -b main https://github.com/nunocoracao/blowfish.git themes/blowfish Follow the theme documentation and make necessary changes for your website.\nTo test the site:\nhugo server --disableFastRender --noHTTPCache Since we are using Obsidian, we need to copy notes from the obsidian folder to the website folder.\ntip I placed notes that I will use for my website under a new folder called posts in the obsidian vault.\nWe can achieve this with the following rsync command:\nrsync -av --delete \u0026#34;src-path\u0026#34; \u0026#34;dest-path\u0026#34; If there is an image on the note, it will not be parsed but only the wikilink for the image will be shown. We need to copy the image to the website folder.\nFirst we need to create an images folder under the static folder.\nmkdir static/images Then we can run this python script and copy the images to our newly created images folder. Replace paths for your directories and make sure that they are correct otherwise the script won\u0026rsquo;t work. Create this script as images.py.\nimport os import re import shutil # Paths posts_dir = \u0026#34;your-post-dir\u0026#34; attachments_dir = \u0026#34;your-image-dir\u0026#34; static_images_dir = \u0026#34;your-static-website-image-dir\u0026#34; base_url_path = \u0026#34;\u0026#34; # Empty for root-hosted site # Step 1: Process each markdown file in the posts directory for filename in os.listdir(posts_dir): if filename.endswith(\u0026#34;.md\u0026#34;): filepath = os.path.join(posts_dir, filename) with open(filepath, \u0026#34;r\u0026#34;) as file: content = file.read() # Step 2: Find all image links in Obsidian format ![Image Description](/images/image.png) images = re.findall(r\u0026#39;!\\[\\[([^]]*\\.png)\\]\\]\u0026#39;, content) # Step 3: Replace image links and ensure URLs are correctly formatted for image in images: # Prepare the Markdown-compatible link with %20 replacing spaces markdown_image = f\u0026#34;![Image Description]({base_url_path}/images/{image.replace(\u0026#39; \u0026#39;, \u0026#39;%20\u0026#39;)})\u0026#34; content = content.replace(f\u0026#34;![[{image}]]\u0026#34;, markdown_image) # Step 4: Copy the image to the Hugo static/images directory if it exists image_source = os.path.join(attachments_dir, image) if os.path.exists(image_source): shutil.copy(image_source, static_images_dir) # Step 5: Write the updated content back to the markdown file with open(filepath, \u0026#34;w\u0026#34;) as file: file.write(content) print(\u0026#34;Markdown files processed and images copied successfully.\u0026#34;) warning Run rsync command before running the image script.\nRun the script:\npython3 images.py You should see the images being copied from your obsidian vault to the static/images folder.\nWe need to create 2 more files. These will be used for creating custom deployment configuration on Cloudflare Workers.\nCreate a wrangler.toml file at the root of your website folder, then paste the following content:\nname = \u0026#34;blog\u0026#34; # use the same name from the workers page compatibility_date = \u0026#34;2026-03-01\u0026#34; # change this to current date [build] command = \u0026#34;./build.sh\u0026#34; [assets] directory = \u0026#34;./public\u0026#34; not_found_handling = \u0026#34;404-page\u0026#34; Create a new script called build.sh, again at the root of your website folder and paste this:\n#!/usr/bin/env bash main() { HUGO_VERSION=0.153.3 # change this to your hugo version export TZ=\u0026#34;Europe/Istanbul\u0026#34; # change this to your timezone # Install Hugo echo \u0026#34;Installing Hugo v${HUGO_VERSION}...\u0026#34; curl -LJO https://github.com/gohugoio/hugo/releases/download/v${HUGO_VERSION}/hugo_extended_${HUGO_VERSION}_linux-amd64.tar.gz tar -xf \u0026#34;hugo_extended_${HUGO_VERSION}_linux-amd64.tar.gz\u0026#34; cp hugo /opt/buildhome rm LICENSE README.md hugo_extended_${HUGO_VERSION}_linux-amd64.tar.gz # Verify installed version echo \u0026#34;Verifgying Hugo installation...\u0026#34; echo Go: \u0026#34;$(go version)\u0026#34; echo Hugo: \u0026#34;$(hugo version)\u0026#34; echo Node: \u0026#34;$(node --version)\u0026#34; # Clone themes repository echo \u0026#34;Cloning themes repository - archie...\u0026#34; git submodule update --init --recursive git config core.quotepath false # Build the site echo \u0026#34;Building the site...\u0026#34; hugo --gc --minify } set -euo pipefail main \u0026#34;$@\u0026#34; Make this script executable:\nchmod +x build.sh Before using Cloudflare workers make sure that you have the latest version of your site available in your remote repo.\nNow that we are done with copying our notes from obsidian to website folder, we can publish it under our domain using Cloudflare Workers Platform.\nAfter logging in to Cloudflare, go to Build -\u0026gt; Compute \u0026amp; AI -\u0026gt; Workers \u0026amp; Pages. Click New Deployment on top-right and select github and finish authorization. Select the website repo and click deploy. This may take some time or the build may fail. If there are any errors, look at the logs and press re-build after fixing the error and pushing the fixed version to the remote repo.\nIf the build is complete then congrats your site is ready to be used. As the last step, you can configure custom domain to point the site to your own domain name. Go to Settings -\u0026gt; Add -\u0026gt; Custom Domain and enter your domain there.\nNow you can visit your domain and see your static website.\nAs a bonus this is the script that combines the process of copying notes, images from the obsidian vault and pushing changes to the remote repo. Create this script at the root of your website folder, you can name it whatever you want, make it executable and run it.\nDon\u0026rsquo;t forget to change directory paths and the repo address.\n#!/bin/bash set -euo pipefail # Change to the script\u0026#39;s directory SCRIPT_DIR=\u0026#34;$(cd \u0026#34;$(dirname \u0026#34;${BASH_SOURCE[0]}\u0026#34;)\u0026#34; \u0026amp;\u0026amp; pwd)\u0026#34; cd \u0026#34;$SCRIPT_DIR\u0026#34; # Set variables for Obsidian to Hugo copy sourcePath=\u0026#34;src-path\u0026#34; destinationPath=\u0026#34;dest-path\u0026#34; # Set GitHub Repo myrepo=\u0026#34;your-remote-repo-address\u0026#34; # Check for required commands for cmd in git rsync python3 hugo; do if ! command -v $cmd \u0026amp;\u0026gt; /dev/null; then echo \u0026#34;$cmd is not installed or not in PATH.\u0026#34; exit 1 fi done # Step 1: Check if Git is initialized, and initialize if necessary if [ ! -d \u0026#34;.git\u0026#34; ]; then echo \u0026#34;Initializing Git repository...\u0026#34; git init git remote add origin $myrepo else echo \u0026#34;Git repository already initialized.\u0026#34; if ! git remote | grep -q \u0026#39;origin\u0026#39;; then echo \u0026#34;Adding remote origin...\u0026#34; git remote add origin $myrepo fi fi # Step 2: Sync posts from Obsidian to Hugo content folder using rsync echo \u0026#34;Syncing posts from Obsidian...\u0026#34; if [ ! -d \u0026#34;$sourcePath\u0026#34; ]; then echo \u0026#34;Source path does not exist: $sourcePath\u0026#34; exit 1 fi if [ ! -d \u0026#34;$destinationPath\u0026#34; ]; then echo \u0026#34;Destination path does not exist: $destinationPath\u0026#34; exit 1 fi rsync -av --delete \u0026#34;$sourcePath\u0026#34; \u0026#34;$destinationPath\u0026#34; # Step 3: Process Markdown files with Python script to handle image links echo \u0026#34;Processing image links in Markdown files...\u0026#34; if [ ! -f \u0026#34;images.py\u0026#34; ]; then echo \u0026#34;Python script images.py not found.\u0026#34; exit 1 fi if ! python3 images.py; then echo \u0026#34;Failed to process image links.\u0026#34; exit 1 fi # Step 4: Add changes to Git echo \u0026#34;Staging changes for Git...\u0026#34; if git diff --quiet \u0026amp;\u0026amp; git diff --cached --quiet; then echo \u0026#34;No changes to stage.\u0026#34; else git add . fi # Step 5: Commit changes with a dynamic message commit_message=\u0026#34;New Blog Post on $(date +\u0026#39;%Y-%m-%d %H:%M:%S\u0026#39;)\u0026#34; if git diff --cached --quiet; then echo \u0026#34;No changes to commit.\u0026#34; else echo \u0026#34;Committing changes...\u0026#34; git commit -m \u0026#34;$commit_message\u0026#34; fi # Step 6: Push all changes to the main branch echo \u0026#34;Deploying to GitHub Main...\u0026#34; if ! git push origin main; then echo \u0026#34;Failed to push to main branch.\u0026#34; exit 1 fi echo \u0026#34;All done! Site synced, processed, committed, built, and deployed.\u0026#34; References:\nhttps://blog.networkchuck.com/posts/my-insane-blog-pipeline/ https://www.youtube.com/watch?v=dnE7c0ELEH8 https://www.youtube.com/watch?v=FZMgUSlNp-0 ","date":"3 January 2026","externalUrl":null,"permalink":"/posts/creating-static-website-from-obsidian-notes/","section":"Posts","summary":"","title":"Creating static website from obsidian notes","type":"posts"},{"content":"","date":"3 January 2026","externalUrl":null,"permalink":"/tags/obsidian/","section":"Tags","summary":"","title":"Obsidian","type":"tags"},{"content":"","externalUrl":null,"permalink":"/authors/","section":"Authors","summary":"","title":"Authors","type":"authors"},{"content":"","externalUrl":null,"permalink":"/categories/","section":"Categories","summary":"","title":"Categories","type":"categories"},{"content":"","externalUrl":null,"permalink":"/series/","section":"Series","summary":"","title":"Series","type":"series"}]