<?xml version="1.0" encoding="UTF-8"?><rss xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:atom="http://www.w3.org/2005/Atom" version="2.0"><channel><title><![CDATA[Anjan's Blog]]></title><description><![CDATA[Anjan's Blog]]></description><link>https://blog.anjann.dev</link><generator>RSS for Node</generator><lastBuildDate>Mon, 20 Apr 2026 22:37:06 GMT</lastBuildDate><atom:link href="https://blog.anjann.dev/rss.xml" rel="self" type="application/rss+xml"/><language><![CDATA[en]]></language><ttl>60</ttl><item><title><![CDATA[[DIY] Scraping Tables From A PDF]]></title><description><![CDATA[Recently I had a problem, scraping the forex card rates from a PDF published by SBI on their website and converting the same to a CSV file. This PDF is updated on the same link every day.
As quoted by the final source where I got the code from -

Exp...]]></description><link>https://blog.anjann.dev/diy-scraping-tables-from-a-pdf</link><guid isPermaLink="true">https://blog.anjann.dev/diy-scraping-tables-from-a-pdf</guid><category><![CDATA[Python]]></category><category><![CDATA[Scraping]]></category><category><![CDATA[pdf]]></category><dc:creator><![CDATA[Anjan Nair]]></dc:creator><pubDate>Sat, 14 Jan 2023 19:03:00 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/stock/unsplash/D9Zow2REm8U/upload/820620df8398fbff0a695b90d91ff3f4.jpeg" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>Recently I had a problem, scraping the forex card rates from a PDF published by SBI on their website and converting the same to a CSV file. This PDF is updated on the same link every day.</p>
<p>As quoted by the final source where I got the code from -</p>
<blockquote>
<p><a target="_blank" href="https://incometaxindia.gov.in/_layouts/15/dit/pages/viewer.aspx?grp=rule&amp;cname=cmsid&amp;cval=103120000000007372&amp;searchfilter=%5B%7B%22crawledpropertykey%22:0,%22value%22:%22income-tax+rules%22,%22searchoperand%22:2%7D,%7B%22crawledpropertykey%22:1,%22value%22:%22rule%22,%22searchoperand%22:2%7D%5D&amp;k=income+tax,income+tax&amp;isdlg=0">Explanation to Rule 26 of the Income Tax Rules, 1962</a> advises using telegraphic transfer rates of SBI as reference for calculating foreign income or capital gains. SBI publishes the rates daily on its website, barring Sundays and bank holidays. Unfortunately, there is no official way to access historical data.</p>
</blockquote>
<h3 id="heading-the-challenge">The Challenge</h3>
<p>Scraping a PDF normally is easy, considering that there are already many open-source projects available for the same. But here, this PDF had data present in tables as shown below -</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1673372575617/3f42def3-555f-4c17-aa82-78765e2aacf6.png" alt class="image--center mx-auto" /></p>
<h3 id="heading-my-approach">My Approach</h3>
<p>Before I found the complete solution to my problem readymade (scroll directly below if you want to skip this), I had made up my mind to build the entire scraper using Node.</p>
<p>I came across many NPM packages to help me with the same, but almost none of them had a built-in approach for parsing tables in a PDF. I struck a gold mine by finally finding an NPM package - <a target="_blank" href="https://github.com/pomgui/pdf-tables-parser"><strong>pomgui/pdf-tables-parser</strong></a><strong>.</strong></p>
<p>It was the exact solution I was looking for, except, as of 10th January 2023 an update to one of its dependencies <a target="_blank" href="https://github.com/mozilla/pdfjs-dist"><strong>pdfjs-dist</strong></a>, the generic build of <a target="_blank" href="https://github.com/mozilla/pdf.js/"><strong>pdfjs</strong></a> caused the package to break.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1673373250778/7e5eb536-6471-40b6-a9f3-4afd2b2459ce.png" alt class="image--center mx-auto" /></p>
<h3 id="heading-the-fix">The Fix</h3>
<p>I would be lying if I said that I fixed the issue in minutes. At first, I thought it was some issue with the <code>node_modules</code> I downloaded and spent some time installing the dependencies I was shown was missing. Only after some time when I went through the source code of the <strong>pdf-tables-parser</strong>, did I realize the path for the build of <strong>pdfjs-dist</strong> had changed from <strong>pdfjs-dist/es5/build/pdf</strong> to <strong>pdfjs-dist/build/pdf</strong>.</p>
<p>A quick fix and update to that had fixed the missing dependencies errors. I put in a <a target="_blank" href="https://github.com/pomgui/pdf-tables-parser/pull/1">pull request</a> on the GitHub repository of the package.</p>
<h3 id="heading-the-code">The Code</h3>
<ol>
<li><p>Downloading the PDF from the website - Using the <code>async await</code> will ensure we can wait for the download asynchronously. The <strong>npm</strong> package <code>download</code> uses a function to pass in the URL and the output folder path.</p>
<pre><code class="lang-javascript"> <span class="hljs-keyword">const</span> download = <span class="hljs-built_in">require</span>(<span class="hljs-string">'download'</span>);
 <span class="hljs-keyword">async</span> <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">fileDownload</span>(<span class="hljs-params"></span>) </span>{
   <span class="hljs-comment">// download(link, output folder)</span>
   <span class="hljs-keyword">await</span> download(<span class="hljs-string">'https://www.sbi.co.in/documents/16012/1400784/FOREX_CARD_RATES.pdf'</span>, <span class="hljs-string">'input'</span>);
 }
</code></pre>
</li>
<li><p>Converting the acquired PDF to JSON - This was made easy after using the <strong>pdf-tables-parser</strong> package.</p>
<p> The code below gets the downloaded PDF from the input folder, extracts the data, and writes it to a <code>report.json</code> file in the <code>out</code> directory.</p>
<pre><code class="lang-javascript"> <span class="hljs-keyword">const</span> { PdfDocument } = <span class="hljs-built_in">require</span>(<span class="hljs-string">'@pomgui/pdf-tables-parser'</span>);
 <span class="hljs-keyword">const</span> fs = <span class="hljs-built_in">require</span>(<span class="hljs-string">'fs'</span>);

 <span class="hljs-keyword">async</span> <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">PDFToJSON</span>(<span class="hljs-params"></span>) </span>{
   <span class="hljs-keyword">const</span> pdf = <span class="hljs-keyword">new</span> PdfDocument();
   <span class="hljs-comment">// pdf.load('path/to/PDF')</span>
   <span class="hljs-keyword">await</span> pdf.load(<span class="hljs-string">'input/FOREX_CARD_RATES.pdf'</span>)
     .then(<span class="hljs-function">() =&gt;</span> fs.writeFileSync(<span class="hljs-string">'out/report.json'</span>, <span class="hljs-built_in">JSON</span>.stringify(pdf, <span class="hljs-literal">null</span>, <span class="hljs-number">2</span>), <span class="hljs-string">'utf8'</span>))
     .catch(<span class="hljs-function">(<span class="hljs-params">err</span>) =&gt;</span> <span class="hljs-built_in">console</span>.error(err));
 }
</code></pre>
<p> Here are the dependencies in the <code>package.json</code> for reference -</p>
<pre><code class="lang-json"> <span class="hljs-string">"dependencies"</span>: {
     <span class="hljs-attr">"@pomgui/pdf-tables-parser"</span>: <span class="hljs-string">"^0.1.0"</span>,
     <span class="hljs-attr">"download"</span>: <span class="hljs-string">"^8.0.0"</span>,
     <span class="hljs-attr">"path"</span>: <span class="hljs-string">"^0.12.7"</span>,
     <span class="hljs-attr">"pdfjs"</span>: <span class="hljs-string">"^2.4.7"</span>,
     <span class="hljs-attr">"pdfjs-dist"</span>: <span class="hljs-string">"^3.2.146"</span>
   }
</code></pre>
<h3 id="heading-the-issue">The Issue</h3>
<p> This code was inefficient as the JSON was not uniform. No doubt that all the contents of the PDF are scraped but it is too ugly and variable to look at.</p>
<p> Here is how the JSON looked</p>
<pre><code class="lang-json"> {
           <span class="hljs-attr">"tableNumber"</span>: <span class="hljs-number">7</span>,
           <span class="hljs-attr">"numrows"</span>: <span class="hljs-number">1</span>,
           <span class="hljs-attr">"numcols"</span>: <span class="hljs-number">1</span>,
           <span class="hljs-attr">"data"</span>: [
             [
               <span class="hljs-string">"UNITED STATES DOLLAR   USD/INR  "</span>
             ]
           ]
         },
         {
           <span class="hljs-attr">"tableNumber"</span>: <span class="hljs-number">8</span>,
           <span class="hljs-attr">"numrows"</span>: <span class="hljs-number">2</span>,
           <span class="hljs-attr">"numcols"</span>: <span class="hljs-number">9</span>,
           <span class="hljs-attr">"data"</span>: [
             [
               <span class="hljs-string">"81.25"</span>,
               <span class="hljs-string">"  82.75"</span>,
               <span class="hljs-string">"  81.18"</span>,
               <span class="hljs-string">"  82.92"</span>,
               <span class="hljs-string">"  80.5"</span>,
               <span class="hljs-string">"  83.2"</span>,
               <span class="hljs-string">"  80.2"</span>,
               <span class="hljs-string">"  83.4"</span>,
               <span class="hljs-string">"  81.12"</span>
             ],
             [
               <span class="hljs-string">"EURO   EUR/INR  "</span>
             ]
           ]
         },
</code></pre>
<h3 id="heading-the-python-fix">The Python Fix</h3>
<p> This amazing GitHub repository solved my entire issue of scraping the said PDF - <a target="_blank" href="https://github.com/sahilgupta/sbi_forex_rates">https://github.com/sahilgupta/sbi_forex_rates</a></p>
<p> Thanks to Sahil's solution the task of scraping had been achieved, leaving me with modifying the existing code to scrape page 1 and upload it to Amazon S3 Bucket.</p>
</li>
<li><p>Connecting to the S3 bucket</p>
<pre><code class="lang-python"> <span class="hljs-keyword">import</span> os
 <span class="hljs-keyword">import</span> boto3
 <span class="hljs-keyword">from</span> dotenv <span class="hljs-keyword">import</span> load_dotenv

 load_dotenv()

 s3 = boto3.client(
     <span class="hljs-string">"s3"</span>,
     aws_access_key_id=os.getenv(<span class="hljs-string">"aws_access_key_id"</span>),
     aws_secret_access_key=os.getenv(<span class="hljs-string">"aws_secret_access_key"</span>),
 )
</code></pre>
</li>
<li><p>Uploading the CSV files</p>
<pre><code class="lang-python"> <span class="hljs-keyword">import</span> pandas <span class="hljs-keyword">as</span> pd

 <span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">upload_csv</span>():</span>
     <span class="hljs-comment"># This loop ensures that all the files and folders in the</span>
     <span class="hljs-comment"># directory "csv_files" is uploaded</span>
     <span class="hljs-keyword">for</span> path, subdirs, files <span class="hljs-keyword">in</span> os.walk(<span class="hljs-string">"csv_files"</span>):
         <span class="hljs-keyword">for</span> name <span class="hljs-keyword">in</span> files:
             hc = pd.read_csv(os.path.join(path, name))
             csv_buf = StringIO()
             hc.to_csv(csv_buf, header=<span class="hljs-literal">True</span>, index=<span class="hljs-literal">False</span>)
             csv_buf.seek(<span class="hljs-number">0</span>)
             s3.put_object(
                 Bucket=os.getenv(<span class="hljs-string">"bucket_name"</span>),
                 Body=csv_buf.getvalue(),
                 Key=os.path.join(path, name),
             )
     print(<span class="hljs-string">"Uploaded CSV to bucket"</span>)
</code></pre>
</li>
<li><p>Uploading the PDF files</p>
<pre><code class="lang-python"> <span class="hljs-comment"># This loop ensures that all the files and folders in the</span>
 <span class="hljs-comment"># directory "pdf_files" is uploaded</span>
 <span class="hljs-keyword">for</span> path, subdirs, files <span class="hljs-keyword">in</span> os.walk(<span class="hljs-string">"pdf_files"</span>):
         <span class="hljs-keyword">for</span> name <span class="hljs-keyword">in</span> files:
             s3.put_object(
                 Bucket=os.getenv(<span class="hljs-string">"bucket_name"</span>),
                 Body=os.path.join(path, name),
                 Key=os.path.join(path, name),
             )
     print(<span class="hljs-string">"Uploaded PDF to bucket"</span>)
</code></pre>
</li>
<li><p>Scraping Page 1 - This is simple, since both the pages are the same all you need is to start from the second index for the table headers and start at the 3rd index for getting the values. Hence, the changes to be made to the <code>dump_data</code> function in the source code will be -</p>
<pre><code class="lang-python"> header_row = lines[<span class="hljs-number">2</span>]
</code></pre>
<p> and</p>
<pre><code class="lang-python"> <span class="hljs-keyword">for</span> line <span class="hljs-keyword">in</span> lines[<span class="hljs-number">3</span>:]:
</code></pre>
</li>
</ol>
<h2 id="heading-concluding-notes">Concluding Notes</h2>
<p>Scraping using Python was an easier task compared with NodeJs. Maybe my approach was wrong when scraping with Node but I felt that scraping with Python felt cleaner and the work got done faster.</p>
<p>You can now schedule a cron job to run the main function/ file to scrape the SBI Forex website. If a change is detected, it will automatically update the CSV.</p>
]]></content:encoded></item><item><title><![CDATA[Sunsetting Rufus - A Glimpse Into The Making]]></title><description><![CDATA[Pre November 28th, 2022 a historic number of applications, bots, websites, etc. were hosted on Heroku for free. My application built using the discord.js module for NodeJs was one of them.
What Motivated Me To Build It?
Short answer - The pandemic.
L...]]></description><link>https://blog.anjann.dev/sunsetting-rufus</link><guid isPermaLink="true">https://blog.anjann.dev/sunsetting-rufus</guid><category><![CDATA[discord.js]]></category><category><![CDATA[rufus]]></category><dc:creator><![CDATA[Anjan Nair]]></dc:creator><pubDate>Sat, 24 Sep 2022 18:04:48 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1664041332147/JoriPxxDT.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>Pre November 28th, 2022 a historic number of applications, bots, websites, etc. were hosted on Heroku for free. My application built using the discord.js module for NodeJs was one of them.</p>
<h1 id="heading-what-motivated-me-to-build-it">What Motivated Me To Build It?</h1>
<p>Short answer - The pandemic.</p>
<p>Long answer - September 26th, 2020 marks my first commit to the GitHub repository which hosts the source code to Rufus. It's been 2 years.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1662740194089/m8VYmvSuP.png" alt="image.png" class="image--center mx-auto" /></p>
<p>Back then I had just gotten started with NodeJs and had a basic idea of what JavaScript was. Discord was fairly popular and was gaining attention due to the pandemic. Everyone was hopping onto Discord to play games together or live stream movies with their loved ones.</p>
<p>I started noticing some issues with the platform back then. It simply was not user-friendly at least at first. If you're totally new to Discord without any exposure to any other similar IRC-type applications you'd have found it overwhelming too. Bots back then had no <strong>slash</strong> commands. Each bot had its own custom <strong>prefix</strong>. </p>
<p>Here is where I started. I started building a guidebook for those new to Discord. Simply call Rufus with the appropriate command and it would guide you. Imagine this to be the Bitwarden of all passwords, remember one prefix/command, and conveniently view the prefixes/commands of other bots. <strong>Convenient right?</strong></p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1662741198015/bfUgOCNom.png" alt="image.png" class="image--center mx-auto" /></p>
<h1 id="heading-the-open-source-way">The Open Source Way</h1>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1664041093921/wILuMcN-0.png" alt="github.png" class="image--center mx-auto" /></p>
<p>With every commit to the source code, the bot started taking shape, new commands started getting added, and more people requested me to add features. The motivation to complete this bot was surreal. I'm not joking when I say this but the constant support from online friends as well as other developers I met during the journey was something I hadn't witnessed before.</p>
<p>A few months after the initial commit I received my first pull request on the repository which felt good because people were beginning to notice the project. From code cleanups to restructuring the code and making it better the open source at GitHub helped mold the project to what it is today.</p>
<p>The bot started gaining stars and I started meeting new developers who built such bots too. It would be useless if I don't mention <a target="_blank" href="https://devshah.xyz/">Dev Shah</a> with whom I had a great time collaborating and writing code. This is someone who at a young age had some really good hold on programming.</p>
<h1 id="heading-ending-note">Ending Note</h1>
<p>Thank you for being a part of this journey. Like every good thing, the chapter of Rufus has come to an end. It is time for me to move ahead. From the 28th of November, Rufus will go dark. There are no plans to rewrite the bot to match the latest Discord API nor are there plans to migrate servers to another hosting service. Nothing can possibly beat the convenience of the free service Heroku offered developers.</p>
<p>If you have read this till here, presenting a list of websites that provide free hosting below. Be free to test them out and then settle on which one can scale up to the project you have.</p>
<p>You can find the GitHub repository for the bot here - <a target="_blank" href="https://github.com/anjannair/Rufus-Discord-Bot">Rufus Discord Bot</a></p>
<p>Sites where you can get free hosting (<a target="_blank" href="https://twitter.com/ajaysharma_dev/status/1559170433865621504">Source</a>) -</p>
<ul>
<li>Netlify.com</li>
<li>Firebase</li>
<li>Vercel.com</li>
<li>GitHubPages</li>
<li>Surge.sh</li>
<li>Render.com</li>
<li>Amazone Web Hosting</li>
<li>Google Cloud Hosting</li>
<li>Glitch.com</li>
<li>Fleek.co</li>
<li>Begin.com</li>
<li>Cloudaccess.net</li>
<li>Infinityfree.net</li>
</ul>
]]></content:encoded></item><item><title><![CDATA[DIY Local Proxy Cache]]></title><description><![CDATA[Proxies, or proxy servers, are computer programs that act as an intermediary between the user and the destination server. One often uses a proxy server to modify the way a user accesses a website, to redirect the user to a different website and so on...]]></description><link>https://blog.anjann.dev/cache-proxy</link><guid isPermaLink="true">https://blog.anjann.dev/cache-proxy</guid><category><![CDATA[proxy]]></category><category><![CDATA[caching]]></category><category><![CDATA[cache]]></category><category><![CDATA[BlogsWithCC]]></category><category><![CDATA[Docker]]></category><dc:creator><![CDATA[Anjan Nair]]></dc:creator><pubDate>Mon, 15 Aug 2022 17:01:03 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1660582342001/ERv23pbrH.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>Proxies, or proxy servers, are computer programs that act as an <strong>intermediary</strong> between the <strong>user</strong> and the <strong>destination server</strong>. One often uses a proxy server to modify the way a user accesses a website, to redirect the user to a different website and so on.</p>
<h2 id="heading-in-this-tutorial">In This Tutorial</h2>
<p>We will be using <code>proxy.py</code> to setup a proxy server to <strong>cache</strong> and serve content from the internet. </p>
<blockquote>
<p>Caching is the process of storing the content of a website in a local cache to speed up the loading of the website.</p>
</blockquote>
<p>Imagine you have a lot of devices on the same network. Now if one device visits a website and the <strong>website is slow to load</strong>, without a proxy all the other devices will have to wait for the website to load.</p>
<p>With a proxy this issue can be <strong>minimized due to the caching of the website</strong>. The proxy server coordinates with the source server to cache documents such as files, images and web pages.</p>
<h2 id="heading-prerequisites">Prerequisites</h2>
<p>⚠️ If you are using a <strong>Mac</strong> system, the Docker image is broken due to incompatibilities with <a target="_blank" href="https://github.com/moby/vpnkit/issues/469">vpnkit</a>. However you can use <a target="_blank" href="https://github.com/abhinavsingh/proxy.py#using-homebrew"><strong>Homebrew</strong></a> to install it.</p>
<p>Having a Raspberry Pi is <strong>not</strong> a requirement for this tutorial. Although if you have a Raspberry Pi you can use it to setup a proxy server.</p>
<p>We will be using <strong>Docker</strong> to setup the proxy server.</p>
<p>Install Docker from here - <a target="_blank" href="https://docs.docker.com/engine/install/">Docker Engine</a></p>
<p>One command is all it will take to setup this proxy.</p>
<pre><code class="lang-bash">docker run -d --restart unless-stopped -p 8899:8899 abhinavsingh/proxy.py:latest --plugins proxy.plugin.CacheResponsesPlugin --hostname 0.0.0.0
</code></pre>
<p><strong>Explanation</strong></p>
<ul>
<li><code>docker run</code> - Starts the proxy server.</li>
<li><code>-d</code> - Runs the proxy server in the background aka detached mode.</li>
<li><code>--restart unless-stopped</code> - Restarts the proxy server if it is stopped automatically, however if stopped automatically it will stop.</li>
<li><code>-p 8899:8899</code> - Sets the port to 8899 and expose the internal port of the Docker container to the internal network.</li>
<li><code>abhinavsingh/proxy.py:latest</code> - The proxy.py image.</li>
<li><code>--plugins proxy.plugin.CacheResponsesPlugin</code> - The plugin to cache responses as mentioned in the documentation <a target="_blank" href="https://github.com/abhinavsingh/proxy.py#cacheresponsesplugin">here</a>.</li>
<li><code>--hostname 0.0.0.0</code> - Sets the hostname to 0.0.0.0, which means that the proxy server will be accessible from any device on the network. This is important.</li>
</ul>
<p>💡If you want to check out more about Docker commands, you can check out my article <a target="_blank" href="https://blog.anjann.dev/docker">here</a>.</p>
<p>Your proxy is now running on port <strong>8899</strong>.</p>
<p>That's about it! You can set your device (recommended) or browser to use the proxy.</p>
<h2 id="heading-setting-it-up">Setting It Up</h2>
<p>Set your proxy IP to the IP address of the <strong>device the docker container is running on</strong>.</p>
<p>If you are running the container on a Mac or Linux system use <code>ifconfig</code> to get the IP address of your system.</p>
<p>If its Windows then <code>ipconfig</code> will print the IP address.</p>
<p>⚠️ Most of the times the DHCP server on your router should assign your device a static internal IP address, however if it doesn't, change your device settings from DHCP assignment to static assignment.</p>
<h3 id="heading-linux">Linux</h3>
<p>If you have a Linux system you will be finding your proxy settings in <strong>Network settings</strong>.</p>
<p>Since I use a Debian based distro I have it in Settings &gt; Network &gt; Proxy.</p>
<h3 id="heading-windows">Windows</h3>
<p>Settings &gt; Network and Internet &gt; Proxy</p>
<h2 id="heading-final-notes">Final Notes</h2>
<p>You can lookup how this proxy server can be used to connect to <strong>multiple devices on the same network</strong>. This was a tutorial for setting it up on one device.</p>
<p>If your router supports it, <strong>configure your router</strong> settings to use the proxy.</p>
]]></content:encoded></item><item><title><![CDATA[Split Large HTML Files]]></title><description><![CDATA[React and many other frameworks have the ability to split big chunks of code into different files to make the work of the programmer easier. What if I tell you the same can be done for HTML static sites too?
Getting Into It
Mind you, I am not the aut...]]></description><link>https://blog.anjann.dev/split-large-html-files</link><guid isPermaLink="true">https://blog.anjann.dev/split-large-html-files</guid><category><![CDATA[HTML5]]></category><dc:creator><![CDATA[Anjan Nair]]></dc:creator><pubDate>Wed, 10 Aug 2022 08:26:24 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1660119926316/W0TkCSoCj.jpeg" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>React and many other frameworks have the ability to split big chunks of code into different files to make the work of the programmer easier. What if I tell you the same can be done for HTML static sites too?</p>
<h2 id="heading-getting-into-it">Getting Into It</h2>
<p>Mind you, I am not the author of this code but just for the sake of documenting everything new I find, I am writing this down. This code has been picked up from a StackOverflow thread (I do not have the link of the original thread but if I do come across it again I will make sure to link it)</p>
<p><strong>The code -</strong></p>
<pre><code class="lang-js"><span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">HTMLImporter</span>(<span class="hljs-params"></span>) </span>{}

HTMLImporter.import = <span class="hljs-function"><span class="hljs-keyword">function</span> (<span class="hljs-params">url</span>) </span>{
  <span class="hljs-keyword">var</span> error, http_request, load, script;

  script =
    <span class="hljs-built_in">document</span>.currentScript || <span class="hljs-built_in">document</span>.scripts[<span class="hljs-built_in">document</span>.scripts.length - <span class="hljs-number">1</span>];

  load = <span class="hljs-function"><span class="hljs-keyword">function</span> (<span class="hljs-params">event</span>) </span>{
    <span class="hljs-keyword">var</span> attribute, index, index1, new_script, old_script, scripts, wrapper;

    wrapper = <span class="hljs-built_in">document</span>.createElement(<span class="hljs-string">"div"</span>);
    wrapper.innerHTML = <span class="hljs-built_in">this</span>.responseText;

    scripts = wrapper.getElementsByTagName(<span class="hljs-string">"SCRIPT"</span>);

    <span class="hljs-keyword">for</span> (index = scripts.length - <span class="hljs-number">1</span>; index &gt; <span class="hljs-number">-1</span>; --index) {
      old_script = scripts[index];

      new_script = <span class="hljs-built_in">document</span>.createElement(<span class="hljs-string">"script"</span>);
      new_script.innerHTML = old_script.innerHTML;

      <span class="hljs-keyword">for</span> (index1 = old_script.attributes.length - <span class="hljs-number">1</span>; index1 &gt; <span class="hljs-number">-1</span>; --index1) {
        attribute = old_script.attributes[index1];
        new_script.setAttribute(attribute.name, attribute.value);
      }

      old_script.parentNode.replaceChild(new_script, old_script);
    }

    <span class="hljs-keyword">while</span> (wrapper.firstChild) {
      script.parentNode.insertBefore(
        wrapper.removeChild(wrapper.firstChild),
        script
      );
    }

    script.parentNode.removeChild(script);

    <span class="hljs-built_in">this</span>.removeEventListener(<span class="hljs-string">"error"</span>, error);
    <span class="hljs-built_in">this</span>.removeEventListener(<span class="hljs-string">"load"</span>, load);
  };

  error = <span class="hljs-function"><span class="hljs-keyword">function</span> (<span class="hljs-params">event</span>) </span>{
    <span class="hljs-built_in">this</span>.removeEventListener(<span class="hljs-string">"error"</span>, error);
    <span class="hljs-built_in">this</span>.removeEventListener(<span class="hljs-string">"load"</span>, load);

    alert(<span class="hljs-string">"there was an error!"</span>);
  };

  http_request = <span class="hljs-keyword">new</span> XMLHttpRequest();
  http_request.addEventListener(<span class="hljs-string">"error"</span>, error);
  http_request.addEventListener(<span class="hljs-string">"load"</span>, load);
  http_request.open(<span class="hljs-string">"GET"</span>, url);
  http_request.send();
};
</code></pre>
<h2 id="heading-how-to-use-it">How to use it</h2>
<p>Import the code into your main HTML file by adding the whole JS code into a file and using the script tag to do so. </p>
<p><strong>Important</strong> - Add the import script at the top before you import your components</p>
<p>After importing the importer code use the following to easily integrate the component into your file -</p>
<pre><code class="lang-js">&lt;script&gt;HTMLImporter.import(<span class="hljs-string">"./path/to/file"</span>)&lt;/script&gt;
</code></pre>
<p>That is it!! You can now split your HTML file into multiple files and yes this works for static websites too!</p>
]]></content:encoded></item><item><title><![CDATA[VeraCrypt - Encrypt Everything]]></title><description><![CDATA[VeraCrypt is a free and open source encryption software. The software can create a virtual encrypted disk that works just like a regular disk but within a file. It can also encrypt a partition or the entire storage device with pre-boot authentication...]]></description><link>https://blog.anjann.dev/veracrypt</link><guid isPermaLink="true">https://blog.anjann.dev/veracrypt</guid><category><![CDATA[encryption]]></category><category><![CDATA[veracrypt]]></category><dc:creator><![CDATA[Anjan Nair]]></dc:creator><pubDate>Wed, 10 Aug 2022 08:20:16 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1660119551996/6tvTqPMdB.jpg" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>VeraCrypt is a free and open source encryption software. The software can create a virtual encrypted disk that works just like a regular disk but within a file. It can also encrypt a partition or the entire storage device with pre-boot authentication.</p>
<h2 id="heading-installation">Installation</h2>
<p>This software is available on Mac OSX, Linux and Windows. Just simply download the installation file from their <a target="_blank" href="https://www.veracrypt.fr/en/Home.html">official site</a>. At the time of writing this was their official site.</p>
<h3 id="heading-for-linux">For Linux</h3>
<p>Since I used Ubuntu my guide will be based on Debian based systems. Just download the Debian/Ubuntu <code>.deb</code> package from the website.</p>
<p>Run the command - </p>
<pre><code class="lang-shell">sudo dpkg -i VeraCrypt_File_Name.deb
</code></pre>
<p>Replace VeraCrypt_File_Name.deb with the name of your downloaded <code>.deb</code> file.</p>
<h3 id="heading-for-windows">For Windows</h3>
<p>A simple <code>.exe</code> installation will work like a charm.</p>
<h2 id="heading-setup">Setup</h2>
<ul>
<li>Get your USB stick ready and plug it into your PC. </li>
<li>Boot up VeraCrypt and click on the <code>Create Volume</code> option.</li>
</ul>
<p>Two options will load:</p>
<p>1) <strong>Create an encrypted file container</strong>: If this option is used VeraCrypt will create a file which will be used as a virtual mount point. A key point to remember here is that once a size is allocated to the file it cannot be increased nor decreased as opposed to the next option.</p>
<p>2) <strong>Create a non-system partition/drive</strong>: If this option is used VeraCrypt will encrypt your whole drive. The size of the drive is the size that will be available to you.</p>
<p>VeraCrypt suggests option 1 but I would say go for 2 if you do not know how much content you plan to encrypt. In this guide we will be going ahead with option 2.</p>
<p>💡 <strong>Note</strong> - <em>If you are interested in option 1: Check out <a target="_blank" href="https://www.youtube.com/watch?v=4SBWc_cQm-Y">this</a> video.</em></p>
<ul>
<li>After selecting your encryption type, it is time to select volume type. The options here are quite understandable. Unless you are possessing very sensitive material and your life is at stake go for the <strong>Standard VeraCrypt Volume</strong>.</li>
</ul>
<p>After selecting your drive aka the USB stick mount location, there are 2 cases here:</p>
<p>1) Your USB stick may be having content already.
2) Your USB stick is empty.</p>
<ul>
<li><p>If your USB stick is empty no worries, in <strong>Volume Creation Mode</strong> select the <em>Create encrypted volume and format it</em> option. If your USB stick has content that you want then select the <em>Encrypt partition in place</em> option.</p>
</li>
<li><p>In <strong>Encryption Options</strong> if you know what you are doing change it to your preference. I left it at the default options as that sufficed me.</p>
</li>
<li><strong>Volume Password</strong> should be something that is strong to avoid dictionary attacks. Usually a unique alphanumeric password should suffice.</li>
<li>💡 <strong>This is important</strong>, if you plan to use your USB stick cross-platform, which you will, it is important that in <strong>Format Options</strong> you should select NTFS. Users who encrypted their partition and did not format it may not get this option.</li>
</ul>
<h3 id="heading-final-steps">Final steps</h3>
<ul>
<li>After all of this is done you will get a interesting window called the <strong>Volume Format</strong>. This will ask you to shake your mouse till a particular bar is full.</li>
<li>Once done a 'Format' option should be available. Click that and depending on the size of your drive and the speed it could take time. My 64GB USB stick took up-to an hour to encrypt.</li>
</ul>
<p>On a conclusive note to the setup I would like to say the <em>Final steps</em> for those who had content in their volume could be different nevertheless, it isn't rocket science to figure it out. </p>
<h2 id="heading-general-use">General Use</h2>
<p>To now use your volume after it has been encrypted, ensure the device you want to use your volume on has VeraCrypt.</p>
<ul>
<li>Open VeraCrypt and click on <code>Select Device</code> and select the encrypted volume mount point.</li>
<li>Select the slot you want it to mount on. On Linux it will show as <code>1</code>,<code>2</code>,<code>3</code>...etc and on Windows it will show as <code>A</code>,<code>B</code>,<code>C</code>...etc.</li>
<li>Click on <code>Mount</code> after selecting the slot and enter your password. Done! Now just navigate to the mount spot and enjoy accessing your files like a normal volume. Once done click on <code>Dismount</code>.</li>
</ul>
<h2 id="heading-videos-used-as-reference">Videos Used As Reference:</h2>
<p>1) <a target="_blank" href="https://www.youtube.com/watch?v=4SBWc_cQm-Y">How To Use VeraCrypt On Linux</a></p>
<p>2) <a target="_blank" href="https://www.youtube.com/watch?v=w0hMYpBpjJM">Linux Tips - Encrypt USB Drive on Windows Linux and MacOS (VeraCrypt)</a></p>
<p>3) <a target="_blank" href="https://www.youtube.com/watch?v=xWDXRH1mWHM">Hide &amp; Encrypt Your Secret Files With VeraCrypt</a></p>
]]></content:encoded></item><item><title><![CDATA[MQTT with ExpressJs]]></title><description><![CDATA[This documentation covers what I had learnt while setting up MQTT with an Express server built using NodeJS. For this project we had written a script to detect a bot (hardware) and continuously track it using YOLO.
Python Code
The camera will keep de...]]></description><link>https://blog.anjann.dev/mqtt-with-expressjs</link><guid isPermaLink="true">https://blog.anjann.dev/mqtt-with-expressjs</guid><category><![CDATA[mqtt]]></category><category><![CDATA[Express.js]]></category><category><![CDATA[web servers]]></category><dc:creator><![CDATA[Anjan Nair]]></dc:creator><pubDate>Wed, 10 Aug 2022 08:18:13 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1660119424973/Lnmpewj5w.webp" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>This documentation covers what I had learnt while setting up MQTT with an Express server built using NodeJS. For this project we had written a script to detect a bot (hardware) and continuously track it using <a target="_blank" href="https://pjreddie.com/darknet/yolo/">YOLO</a>.</p>
<h2 id="heading-python-code">Python Code</h2>
<p>The camera will keep detecting the bot and track it, when the bot reaches its sticker the camera should run a post request in Python.</p>
<p>The script for the python script is as follows -</p>
<pre><code class="lang-py"><span class="hljs-keyword">import</span> requests
endpoint = <span class="hljs-string">'http://localhost:3000/send-mqtt'</span>
data = {
    <span class="hljs-string">'message'</span>: <span class="hljs-string">'Right'</span>
}
r = requests.post(url=endpoint, data=data)
</code></pre>
<p>If one is connecting via another device they have to simply replace <code>localhost</code> with the IP address of the device the server is being run on.</p>
<p>IP of the server can be found by typing in <code>ipconfig</code> in Windows and scrolling to the <code>IPv4</code> section.</p>
<p><strong>Note - If the script gives an error saying the module requests is not found just run <code>pip install requests</code></strong>.</p>
<h2 id="heading-the-server">The Server</h2>
<p>The broker used for sending messages is <strong>mosquitto</strong>. The url is as follows: <code>mqtt://test.mosquitto.org</code>. The following server is open to anyone hence sensitive messages should be avoided.</p>
<h3 id="heading-using-nodejs">Using NodeJs</h3>
<p>The first steps involve installing Node itself from their website <a target="_blank" href="https://nodejs.dev/">here</a>.</p>
<p>After Node is setup in a new folder run the following line of code:
<code>npm init</code></p>
<p>Fill in all the details or just keep hitting enter till a <code>package.json</code> file is formed.</p>
<p>Proceed to install the <strong>express, body-parser and mqtt</strong> packages by using the following command:
<code>npm i express body-parser mqtt</code></p>
<p>If somehow this command fails install each separately in the following format: <code>npm i &lt;package name&gt;</code></p>
<h3 id="heading-server-code">Server Code</h3>
<pre><code class="lang-js"><span class="hljs-keyword">var</span> express = <span class="hljs-built_in">require</span>(<span class="hljs-string">"express"</span>);
<span class="hljs-keyword">var</span> bodyParser = <span class="hljs-built_in">require</span>(<span class="hljs-string">"body-parser"</span>);
<span class="hljs-keyword">var</span> app = express();
<span class="hljs-keyword">var</span> mqttHandler = <span class="hljs-built_in">require</span>(<span class="hljs-string">"./mqtt_handler"</span>);

app.use(express.json());
app.use(express.urlencoded({ <span class="hljs-attr">extended</span>: <span class="hljs-literal">true</span> }));

<span class="hljs-keyword">var</span> mqttClient = <span class="hljs-keyword">new</span> mqttHandler();
mqttClient.connect();

<span class="hljs-comment">// Routing</span>
app.post(<span class="hljs-string">"/send-mqtt"</span>, <span class="hljs-function"><span class="hljs-keyword">function</span> (<span class="hljs-params">req, res</span>) </span>{
  mqttClient.sendMessage(req.body.message);
  res.status(<span class="hljs-number">200</span>).send(<span class="hljs-string">"Message sent to mqtt"</span>);
});

<span class="hljs-keyword">var</span> server = app.listen(<span class="hljs-number">3000</span>, <span class="hljs-function"><span class="hljs-keyword">function</span> (<span class="hljs-params"></span>) </span>{
  <span class="hljs-built_in">console</span>.log(<span class="hljs-string">"App running on port."</span>, server.address().port);
});
</code></pre>
<h3 id="heading-mqtt-handler">MQTT Handler</h3>
<pre><code class="lang-js"><span class="hljs-keyword">const</span> mqtt = <span class="hljs-built_in">require</span>(<span class="hljs-string">"mqtt"</span>);

<span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">MqttHandler</span> </span>{
  <span class="hljs-keyword">constructor</span>() {
    <span class="hljs-built_in">this</span>.mqttClient = <span class="hljs-literal">null</span>;
    <span class="hljs-built_in">this</span>.host = <span class="hljs-string">"mqtt://test.mosquitto.org"</span>;
    <span class="hljs-built_in">this</span>.username = <span class="hljs-string">"YOUR_USER"</span>; <span class="hljs-comment">// mqtt credentials if these are needed to connect</span>
    <span class="hljs-built_in">this</span>.password = <span class="hljs-string">"YOUR_PASSWORD"</span>;
  }

  connect() {
    <span class="hljs-comment">// Connect mqtt with credentials (in case of needed, otherwise we can omit 2nd param)</span>
    <span class="hljs-built_in">this</span>.mqttClient = mqtt.connect(<span class="hljs-built_in">this</span>.host);
    <span class="hljs-comment">// , { username: this.username, password: this.password }</span>
    <span class="hljs-comment">// Mqtt error calback</span>
    <span class="hljs-built_in">this</span>.mqttClient.on(<span class="hljs-string">"error"</span>, <span class="hljs-function">(<span class="hljs-params">err</span>) =&gt;</span> {
      <span class="hljs-built_in">console</span>.log(<span class="hljs-string">"An error has occured: "</span>, err);
      <span class="hljs-built_in">this</span>.mqttClient.end();
    });

    <span class="hljs-comment">// Connection on success</span>
    <span class="hljs-built_in">this</span>.mqttClient.on(<span class="hljs-string">"connect"</span>, <span class="hljs-function">() =&gt;</span> {
      <span class="hljs-built_in">console</span>.log(<span class="hljs-string">`Mqtt client connected`</span>);
    });

    <span class="hljs-comment">// Mqtt subscriptions</span>
    <span class="hljs-built_in">this</span>.mqttClient.subscribe(<span class="hljs-string">"mytopic"</span>, { <span class="hljs-attr">qos</span>: <span class="hljs-number">0</span> });

    <span class="hljs-comment">// Logging a message when it arives</span>
    <span class="hljs-built_in">this</span>.mqttClient.on(<span class="hljs-string">"message"</span>, <span class="hljs-function"><span class="hljs-keyword">function</span> (<span class="hljs-params">topic, message</span>) </span>{
      <span class="hljs-keyword">if</span> (!message.toString().startsWith(<span class="hljs-string">"32333"</span>)) {
        <span class="hljs-built_in">console</span>.log(message.toString());
      }
    });

    <span class="hljs-built_in">this</span>.mqttClient.on(<span class="hljs-string">"close"</span>, <span class="hljs-function">() =&gt;</span> {
      <span class="hljs-built_in">console</span>.log(<span class="hljs-string">`mqtt client disconnected`</span>);
    });
  }

  <span class="hljs-comment">// Sends a mqtt message to topic: mytopic</span>
  sendMessage(message) {
    <span class="hljs-built_in">this</span>.mqttClient.publish(<span class="hljs-string">"mytopic"</span>, message);
  }
}

<span class="hljs-built_in">module</span>.exports = MqttHandler;
</code></pre>
]]></content:encoded></item><item><title><![CDATA[MongoDB Installation on Linux]]></title><description><![CDATA[A small guide to install MongoDB Community on Ubuntu. This guide was created after a minor inconvenience I faced to get the server up and running.
Getting Started
The starting few steps can be found here on their website
Starting the mongodb server -...]]></description><link>https://blog.anjann.dev/mongodb-installation-on-linux</link><guid isPermaLink="true">https://blog.anjann.dev/mongodb-installation-on-linux</guid><category><![CDATA[MongoDB]]></category><category><![CDATA[Linux]]></category><dc:creator><![CDATA[Anjan Nair]]></dc:creator><pubDate>Wed, 10 Aug 2022 08:15:52 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/unsplash/cijiWIwsMB8/upload/v1660118354334/Z5R0y5BGQ.jpeg" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>A small guide to install MongoDB Community on Ubuntu. This guide was created after a minor inconvenience I faced to get the server up and running.</p>
<h2 id="heading-getting-started">Getting Started</h2>
<p>The starting few steps can be found <a target="_blank" href="https://docs.mongodb.com/manual/tutorial/install-mongodb-on-ubuntu/">here</a> on their website</p>
<p>Starting the mongodb server - </p>
<pre><code class="lang-shell">sudo service mongod start
</code></pre>
<p>Checking the status of the server - </p>
<pre><code class="lang-shell">sudo systemctl status mongod
</code></pre>
<p>Stopping the mongodb server - </p>
<pre><code class="lang-shell">sudo service mongod stop
</code></pre>
<h2 id="heading-error-faced">Error faced</h2>
<p>On trying to get the server status I always got the following error - 
<code>Active: failed (Result: exit-code)</code></p>
<p>I tried all the troubleshooting guides until I stumbled on a StackOverflow answer detailing the solution to this
Just do those two commands for temporary solution:</p>
<pre><code class="lang-shell">sudo rm -rf /tmp/mongodb-27017.sock
sudo service mongod start
</code></pre>
<h3 id="heading-cause-of-the-error">Cause of the error</h3>
<p>For details:</p>
<p>That shall be fault due to user permissions in .sock file, You may have to change the owner to monogdb user.</p>
<pre><code class="lang-shell">chown -R mongodb:mongodb /var/lib/mongodb
chown mongodb:mongodb /tmp/mongodb-27017.sock
</code></pre>
<h2 id="heading-uninstall-mongodb-completely">Uninstall MongoDB completely</h2>
<pre><code class="lang-shell">sudo service mongod stop
sudo apt-get purge mongodb-org*
sudo rm -r /var/log/mongodb
sudo rm -r /var/lib/mongodb
</code></pre>
]]></content:encoded></item><item><title><![CDATA[Xilinx Setup on Ubuntu]]></title><description><![CDATA[Xilinx has been one software used to program the FPGA board in Verilog. Currently (at the time of writing) quite funnily this software fails to install in Windows 11 and has some issues installing in Windows 10. Linux however does not fail us here. T...]]></description><link>https://blog.anjann.dev/xilinx-setup-on-ubuntu</link><guid isPermaLink="true">https://blog.anjann.dev/xilinx-setup-on-ubuntu</guid><category><![CDATA[Ubuntu]]></category><category><![CDATA[xilinx]]></category><category><![CDATA[vivado]]></category><dc:creator><![CDATA[Anjan Nair]]></dc:creator><pubDate>Wed, 10 Aug 2022 07:58:18 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1660118236899/skOMJwCkw.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>Xilinx has been one software used to program the FPGA board in Verilog. Currently (at the time of writing) quite funnily this software fails to install in Windows 11 and has some issues installing in Windows 10. Linux however does not fail us here. This tutorial has been picked up from <a target="_blank" href="https://www.youtube.com/watch?v=yzEIQLQZYpk">this</a> YouTube tutorial but with a few changes.</p>
<h2 id="heading-first-step">First Step</h2>
<p>As of 2022 in April the latest version of Xilinx is 14.7. Head over <a target="_blank" href="https://www.xilinx.com/support/download/index.html/content/xilinx/en/downloadNav/vivado-design-tools/archive-ise.html">here</a> and click on 14.7, scroll down to the ISE Design Suite and install the <strong>Full Installer for Linux</strong>. A simple popup to sign-up/in will come up.</p>
<h2 id="heading-extract-the-contents">Extract the contents</h2>
<p>After the download is complete extract the file using the following command - </p>
<pre><code class="lang-shell">tar -xvf Xilinx_ISE_DS_Lin_14.7_1015_1.tar
</code></pre>
<p>This extracts the whole tar file.</p>
<h2 id="heading-running-the-setup">Running The Setup</h2>
<p>To further setup the Xilinx software we run the following - </p>
<pre><code class="lang-shell">sudo ./xsetup
</code></pre>
<p>This will run the setup!</p>
<h3 id="heading-error-fixing">Error fixing</h3>
<p>During this you may run into an issue which says <em>libncurses.so.5: cannot open shared object file: No such file or directory</em>. A fix to this is done by running </p>
<pre><code class="lang-shell">sudo apt-get install libncurses5
</code></pre>
<h2 id="heading-next-steps">Next Steps</h2>
<p>The next steps are intuitive and can easily be figured out by clicking <strong>Next</strong></p>
<h2 id="heading-finally-running-it">Finally Running It</h2>
<p>Run this command first - </p>
<pre><code class="lang-shell">source /opt/Xilinx/14.7/ISE_DS/settings64.sh
</code></pre>
<p>and then just type</p>
<pre><code class="lang-shell">ise
</code></pre>
<p>in the command line. You are good to go!</p>
<h3 id="heading-error-i-faced">Error I faced</h3>
<p>The video mentioned above has a step involving adding the <em>source..</em> command to <em>.bashrc</em>. This led to errors as well as problems on my bash terminal. So avoid it and follow the step mentioned above.</p>
]]></content:encoded></item><item><title><![CDATA[Spotify AdBlock On Linux]]></title><description><![CDATA[Spotify's ad blocking version is available all the way from Android to Windows but for Linux a special repository exists just for this purpose.
Pre-requisites

Install Spotify for your Linux distro by going to the official site of Spotify and not ins...]]></description><link>https://blog.anjann.dev/spotify-adblock-on-linux</link><guid isPermaLink="true">https://blog.anjann.dev/spotify-adblock-on-linux</guid><category><![CDATA[adblock]]></category><category><![CDATA[Linux]]></category><category><![CDATA[Spotify]]></category><dc:creator><![CDATA[Anjan Nair]]></dc:creator><pubDate>Wed, 10 Aug 2022 07:56:34 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/unsplash/H4fYXZ1hyco/upload/v1660118144951/_Gdjh6YTr.jpeg" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>Spotify's ad blocking version is available all the way from Android to Windows but for Linux a special <a target="_blank" href="https://github.com/abba23/spotify-adblock">repository</a> exists just for this purpose.</p>
<h2 id="heading-pre-requisites">Pre-requisites</h2>
<ol>
<li>Install Spotify for your Linux distro by going to the official <a target="_blank" href="https://www.spotify.com/us/download/linux/">site</a> of Spotify and <strong>not</strong> installing the package using Snap but using the Debian package installation.</li>
<li>Download Git from <a target="_blank" href="https://git-scm.com/downloads">here</a>.</li>
<li><code>Make</code> may already be installed on your device but you can install it using <code>sudo apt-get install build-essential</code> to be on the safe side.</li>
<li>Install <code>Rust</code> using the following command - <code>curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh</code></li>
</ol>
<p>That's about it!</p>
<h2 id="heading-installation">Installation</h2>
<p>To finally install the adblocked version run the following commands on your terminal - </p>
<pre><code class="lang-bash">git <span class="hljs-built_in">clone</span> https://github.com/abba23/spotify-adblock.git
<span class="hljs-built_in">cd</span> spotify-adblock
make
</code></pre>
<p>Finally run - <code>sudo make install</code></p>
<p>To test if it works run this command - <code>LD_PRELOAD=/usr/local/lib/spotify-adblock.so spotify</code></p>
<p>This should now launch Spotify with the adblocked version!</p>
<h3 id="heading-creating-a-desktop-file">Creating a desktop file</h3>
<p>If you run Spotify installed by you, you will notice the ads aren't blocked unless you run the command above on the terminal. To solve this issue a small hack exists.</p>
<ul>
<li>Fire up your terminal and change your directory on the terminal - <code>cd ~/.local/share/applications</code></li>
<li>You now need to create a desktop file in order to get the application in your menu. Create the file and edit it by using - <code>nano spotify-adblock.desktop</code></li>
<li>In the editing space copy paste the following code: <pre><code>  <span class="hljs-section">[Desktop Entry]</span>
  <span class="hljs-attr">Type</span>=Application
  <span class="hljs-attr">Name</span>=Spotify (adblock)
  <span class="hljs-attr">GenericName</span>=Music Player
  <span class="hljs-attr">Icon</span>=spotify-client
  <span class="hljs-attr">TryExec</span>=spotify
  <span class="hljs-attr">Exec</span>=env LD_PRELOAD=/usr/local/lib/spotify-adblock.so spotify %U
  <span class="hljs-attr">Terminal</span>=<span class="hljs-literal">false</span>
  <span class="hljs-attr">MimeType</span>=x-scheme-handler/spotify<span class="hljs-comment">;</span>
  <span class="hljs-attr">Categories</span>=Audio<span class="hljs-comment">;Music;Player;AudioVideo;</span>
  <span class="hljs-attr">StartupWMClass</span>=spotify
</code></pre></li>
<li>Ctrl + X, Y and hit enter to exit and save the file!</li>
</ul>
<p>Done! Now head over to your application menu and you will be seeing a new desktop icon called <strong>Spotify (adblock)</strong></p>
<h2 id="heading-caveats">Caveats</h2>
<ol>
<li>The app does not minimize to dock even if you enable the setting instead it will just shut down. Hence you will have to minimize it and keep it in your app tray.</li>
<li>Apart from ad blocking this mod does not offer any other premium features like downloading. (unlimited skips are automatically enabled in this mod)</li>
</ol>
]]></content:encoded></item><item><title><![CDATA[Ubuntu Setup and Configuration]]></title><description><![CDATA[This tutorial is made from what I learnt during my experience of setting up Ubuntu on my virtual machine as well as dual booting. If you are interested in knowing how to setup Ubuntu on VMware the tutorial is right under this paragraph. To setup for ...]]></description><link>https://blog.anjann.dev/ubuntu-setup-and-configuration</link><guid isPermaLink="true">https://blog.anjann.dev/ubuntu-setup-and-configuration</guid><category><![CDATA[Ubuntu]]></category><category><![CDATA[Dual booting]]></category><category><![CDATA[vmware]]></category><dc:creator><![CDATA[Anjan Nair]]></dc:creator><pubDate>Wed, 10 Aug 2022 07:51:23 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/unsplash/4Mw7nkQDByk/upload/v1660117486047/yiGAcSTVG.jpeg" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>This tutorial is made from what I learnt during my experience of setting up Ubuntu on my virtual machine as well as dual booting. If you are interested in knowing how to setup Ubuntu on VMware the tutorial is right under this paragraph. To setup for dual boot you'd find it below the VMware setup instructions.</p>
<p><strong>Quick Note</strong> - Check out Pop!_OS if you are still deciding distros. Thank me later!</p>
<h2 id="heading-vmware">VMware</h2>
<p>Install the VMware Workstation first from the website (one can optionally get the keys from GitHub and get the Pro version for free).</p>
<p>Download the Ubuntu iso (in my case Windows) on the host machine which can be found on the official Ubuntu site.</p>
<p>Open VMware and create new VM followed by these steps:</p>
<ul>
<li>Will install OS later</li>
<li><code>Linux - Ubuntu 64bit</code></li>
<li>Name the VM whatever you like</li>
<li>Give it at least 40GB and it stays in split</li>
<li>Customize Hardware:- 4GB RAM minimum, Cores - 4, New CD/DVD =&gt; Select iso image, Finish</li>
<li>Play VM =&gt; Software update download and install</li>
<li>Install Ubuntu</li>
<li>Minimal Install (If preferred you can go for the other one but I did not require that)</li>
<li>Check the "Install third party checkbox"</li>
<li>Erase and install Ubuntu (don't worry your data on your host PC will not be affected if this option is clicked)</li>
</ul>
<p>Rest steps are easy and configurable on your own.</p>
<p>Let it install, it takes some time and on completion a success message is shown. Once that is done a prompt to restart the VM will be shown and you are done! </p>
<p>To sync Ubuntu's calendar with your Google Calendar you can login with Google too!</p>
<h3 id="heading-some-fixes-i-had-to-search-for">Some Fixes I had To Search For</h3>
<ol>
<li>In the minimalist version the codecs for audio was missing so you have to install ffmpeg using sudo from the CLI.</li>
<li>To activate the camera from VMware, when on full screen mode hover to the top and find the camera icon near the notes and right click on it. This will present a option to Enable the camera. Wait for some time and if it gives an error try again. On your second or third try it should work after giving a prompt. </li>
<li>Use the update command to find for updates and upgrade to upgrade the packages.</li>
</ol>
<h2 id="heading-dual-boot-windows">Dual Boot (Windows)</h2>
<p>Some pre-requisites for doing this include -</p>
<ul>
<li>A good USB flash drive (4 GB minimum to install)</li>
<li><a target="_blank" href="https://www.balena.io/etcher/">Balena Etcher</a> installed on Windows or <a target="_blank" href="https://www.ventoy.net/en/index.html">Ventoy</a> (recommended)</li>
<li>The latest Ubuntu <code>.iso</code> file downloaded and ready</li>
</ul>
<p><strong>Although the above method mentions Balena Etcher, I feel that should be substituted with <a target="_blank" href="https://www.ventoy.net/en/index.html">Ventoy</a>.</strong></p>
<p>Once you have the above mentioned items ready all you have to do is fire up Etcher with the USB plugged in to your device. Etcher will automatically detect your USB. On rare circumstances if Etcher does not detect your USB, you should check if the USB is being detected by your device first.</p>
<p>A <strong>FLASH</strong> option should appear when everything is ready. Flashing takes some time and once done you are good to go!</p>
<p>Head over to Windows and search for <strong>Disk Management</strong> and create a new partition to install Ubuntu in. A minimum of 40GB is recommended, although I had allocated 100GB. </p>
<p>Note - The steps mentioned below are for Dell devices, your BIOS menu and settings may be different so do refer their documentations if the need be. Although the steps mentioned below shouldn't defer depending on device.</p>
<p>The following steps have to be done in order to dual boot Ubuntu - </p>
<ul>
<li>Reboot your device and when it is just about to startup press <code>F12</code> or whichever shortcut takes you into <code>BIOS</code> mode.</li>
<li>Once into <code>BIOS</code> mode, in <em>Boot Menu</em> or in <em>Boot Devices</em> you will find your USB device name mentioned. Click on that and wait for the screen to load.</li>
<li>Once the Ubuntu screen loads click on <code>Install Ubuntu</code>.</li>
<li>Select <code>Normal Installation</code> and you can then optionally install third-party software for graphics etc.</li>
<li>This step is the most important step of all. When the option of <code>Installation Type</code> is asked, click on <code>Something else</code>, find for your partition allocated for Ubuntu in the GUI. In the popup select <code>Primary</code> as the type of partition, set the use as <code>Ext4 journaling file system</code> and finally the mount point should be set as <code>/</code>. Verify if the <code>Windows Boot Manager</code> has the type <code>efi</code> and then go ahead and click on <strong>Install Now</strong>.</li>
<li>The next few steps are self-explanatory and the installation should now take place.</li>
</ul>
<p>Now on rebooting your device you shall notice that a bootloader called GRUB starts up. On selecting <code>Ubuntu</code>, <code>Ubuntu</code> shall load and on selecting <code>Windows Boot Manager</code> Windows will load.</p>
<h3 id="heading-issues-faced-in-dual-boot">Issues Faced In Dual Boot</h3>
<p>One major issue faced when installing Ubuntu on Dell was that I had to turn off RST.</p>
<p><img src="https://res.cloudinary.com/practicaldev/image/fetch/s--HmcbQ6-A--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/i/3opvjmf0c8bj7k6yylek.png" alt="Error Image" /></p>
<p><a target="_blank" href="https://dev.to/lakshmiwarrier/dual-booting-windows-10-and-ubuntu-20-04-with-rst-issue-fixed-4le8">This</a> article provides a great solution to fix the issue. However if you lazy to read the article here are the steps to fix the issue -</p>
<ul>
<li>Open CMD in Windows (<code>Run as administrator</code>) and type in <code>bcdedit /set {current} safeboot minimal</code> 
<img src="https://res.cloudinary.com/practicaldev/image/fetch/s--mNMByZgU--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/i/nyyj81pvyv70ld73hhun.png" alt="Command Image" /></li>
<li>Go into BIOS setup and search for <code>SATA mode</code> and change the current setting from whatever it was to <code>AHCI</code>. A frightening question can be asked: <code>All existing data stored on the drives will be erased when resetting the controller mode. Do you want to proceed? [Yes] [No]</code> just click on <code>Yes</code> without worrying. Press <code>F10</code> and it will save and exit from <code>BIOS Setup</code>. </li>
<li>The computer will startup in <code>safe boot mode</code>. Open CMD and type the following - <code>bcdedit /deletevalue {current} safeboot</code>
<img src="https://res.cloudinary.com/practicaldev/image/fetch/s--v09mfloh--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/i/mq28k2149ug3j4qoxl7n.PNG" alt="Second CMD Image" /></li>
</ul>
<p>And done! Your Ubuntu should start without any issues now!</p>
<h3 id="heading-personal-setup">Personal Setup</h3>
<ul>
<li><a target="_blank" href="https://github.com/abba23/spotify-adblock">Spotify</a> without ads :)</li>
<li><a target="_blank" href="https://github.com/AdisonCavani/distro-grub-themes">Grub</a> themes.</li>
<li><a target="_blank" href="https://kdeconnect.kde.org/">KDE Connect</a> to connect it with your phone.</li>
<li><a target="_blank" href="https://itsfoss.com/gnome-tweak-tool/">Gnome Tweaks</a></li>
</ul>
]]></content:encoded></item><item><title><![CDATA[Hello Linux, Goodbye Windows]]></title><description><![CDATA[I had an old PC (32-bit and 2GB RAM) which had Windows 7 on it. The PC was slow and used to take a lot of time to load anything that required some computational power.
What Was The Idea?
The idea was to replace Windows 7 with a more secure OS as upgr...]]></description><link>https://blog.anjann.dev/linux-on-old-pc</link><guid isPermaLink="true">https://blog.anjann.dev/linux-on-old-pc</guid><category><![CDATA[Linux]]></category><category><![CDATA[old desktop]]></category><category><![CDATA[q4os]]></category><dc:creator><![CDATA[Anjan Nair]]></dc:creator><pubDate>Wed, 10 Aug 2022 07:43:46 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/unsplash/6rkJD0Uxois/upload/v1660117337106/OyMKmDIX6.jpeg" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>I had an old PC (32-bit and 2GB RAM) which had Windows 7 on it. The PC was slow and used to take a lot of time to load anything that required some computational power.</p>
<h3 id="heading-what-was-the-idea">What Was The Idea?</h3>
<p>The idea was to replace Windows 7 with a more secure OS as upgrading the Windows version would have been difficult.</p>
<h3 id="heading-which-os">Which OS?</h3>
<p>To select the OS at first I decided to go with <a target="_blank" href="https://zorin.com/os/download/15/lite/">Zorin lite</a> which has 32 bit support. After flashing my USB and booting it up, everything functioned perfectly until the install button was clicked.</p>
<p>After nearly 4 tries and even being connected on a reasonable 50Mbps speed the installation did not continue and the installer always got hung at the <em>Install updates while installing Zorin</em> screen.</p>
<p>This led me to finally conclude that Zorin OS may not be compatible with the PC I have.</p>
<p>After a little research and finding I struck gold when I found a good alternative to the Windows 7 environment - <a target="_blank" href="https://q4os.org/">Q4OS</a>. At first I was skeptical about this too but it worked wonders.</p>
<h3 id="heading-about-q4oss-installation-process">About Q4OS's Installation Process</h3>
<p>It started with a blank blue screen which did scare me at first. Later the installation had 2 options <strong>Manual</strong> and <strong>Automatic</strong>. As I always have believed that manual is always preferable as one gets to chose what they require and don't require.</p>
<p>After selecting manual, a few more steps later and after around 10 minutes I managed to clean the disk that had Windows on it and substitute it with Q4OS.</p>
<p>There would be some issues during the installation for example the screen may just get frozen at a blue screen but after a few minutes the installation resumes normally.</p>
<h3 id="heading-conclusion">Conclusion</h3>
<p>In conclusion Q4OS is a good alternative for PC's that cannot run Windows higher than 7. This OS can be used to bring old PC's back to life. Lastly Q4OS is an operating system based on Debian.
<br />
<br />
<br /></p>
<div>
<img src="https://upload.wikimedia.org/wikipedia/commons/thumb/a/aa/Q4OS_logo.svg/1280px-Q4OS_logo.svg.png" />
</div>]]></content:encoded></item><item><title><![CDATA[Elementary OS Setup]]></title><description><![CDATA[This tutorial gives you an insight into how I switched from Windows to ElementaryOS.
Getting Started
Ensure you download the .iso file from their website. Scroll down to the download section, if you wish to pay you can pay, else click on custom and e...]]></description><link>https://blog.anjann.dev/elementary-os-setup</link><guid isPermaLink="true">https://blog.anjann.dev/elementary-os-setup</guid><category><![CDATA[Linux]]></category><category><![CDATA[Dual booting]]></category><category><![CDATA[elementary OS]]></category><dc:creator><![CDATA[Anjan Nair]]></dc:creator><pubDate>Wed, 10 Aug 2022 07:40:35 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1660117148863/Xq4JlKPWw.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>This tutorial gives you an insight into how I switched from Windows to ElementaryOS.</p>
<h2 id="heading-getting-started">Getting Started</h2>
<p>Ensure you download the <em>.iso</em> file from their <a target="_blank" href="https://elementary.io/">website</a>. Scroll down to the download section, if you wish to pay you can pay, else click on custom and enter the amount as 0. Once that is done the download will be initiated.</p>
<h3 id="heading-the-installation">The Installation</h3>
<p>The same instructions are mentioned on their installation guide over <a target="_blank" href="https://elementary.io/docs/installation#installation">here</a>. Be sure to check the specifications required for the USB flash drive as well as the system.</p>
<h3 id="heading-flashing">Flashing</h3>
<p>Once the image file is downloaded Etcher does the magic. The process takes some time so hold tight.</p>
<h3 id="heading-booting">Booting</h3>
<p>The device used to install Elementary in my case was Dell hence on booting <strong>F12</strong> was enough to enter Boot Mode. Once you are in boot mode the USB should be visible in the options on the left (varies per device). Click on it and voila!</p>
<ul>
<li>If you are booting from the EOS image for the first time let it run a file check. Once the check is done a basic setup screen will pop-up. Select the clear drive option and wait for some time. </li>
<li>Optionally the demo mode can be tried on first to check how your device suits with EOS.</li>
<li>After the initial setup is done and an account is setup you are ready to go!!</li>
</ul>
<p>NOW THE NEXT TIME YOU POWER ON YOUR DEVICE, IT SHOULD AUTOMATICALLY START EOS!</p>
<p>(Side note - You can eject the USB and remove it now)</p>
<h2 id="heading-initial-tips-and-tricks">Initial tips and tricks</h2>
<ul>
<li>Firefox or your preferred browser can be installed from <a target="_blank" href="https://flathub.org/home">FlatHub</a>. All major apps can be found there.</li>
<li>The weird close and maximize button can be tweaked too using this <a target="_blank" href="https://github.com/pantheon-tweaks/pantheon-tweaks">Pantheon Tweaks</a> package </li>
<li>In settings I noticed the <strong>Location</strong> permission is on by default, one can fix that by switching it off.</li>
<li>Desktop folders has an app called DesktopFolder which can usually be installed using the AppCentre but it has not been optimized for EOS6 hence a pull request fixing the error can be found in <a target="_blank" href="https://github.com/spheras/desktopfolder/pull/328">this</a> pull request (as of October 2021).</li>
</ul>
<h2 id="heading-an-update">An Update</h2>
<p>So I removed ElementaryOS because it was giving me the following issues -</p>
<ol>
<li>Audio issues when connected without an external mic</li>
<li>Battery drain even suspended aka put to sleep</li>
<li>A little heating issue</li>
<li>Slower boot (startup) when compared to Windows</li>
<li>No audio compartmentalization, meaning audio from one app is not isolated, instead, that audio is even heard in the other app.</li>
</ol>
<p>These issues may seem trivial but it was like a needle that one cannot find in the middle of a very soft and comfortable bed.</p>
<p>Hence ElementaryOS isn't an distro I would recommend for someone who has a laptop.</p>
]]></content:encoded></item><item><title><![CDATA[Imagining Daemons]]></title><description><![CDATA[There's a saying - 'The devil is at his strongest while we're looking the other way.'
Like a program running in the background silently. While we're busy doing other shit. 'Daemons,' they call them. They perform action without user interaction. Monit...]]></description><link>https://blog.anjann.dev/daemons</link><guid isPermaLink="true">https://blog.anjann.dev/daemons</guid><category><![CDATA[Linux]]></category><category><![CDATA[daemon]]></category><dc:creator><![CDATA[Anjan Nair]]></dc:creator><pubDate>Wed, 10 Aug 2022 07:38:09 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1660116904815/6b0eVJwW8.jpg" length="0" type="image/jpeg"/><content:encoded><![CDATA[<blockquote>
<p>There's a saying - 'The devil is at his strongest while we're looking the other way.'</p>
<p>Like a program running in the background silently. While we're busy doing other shit. 'Daemons,' they call them. They perform action without user interaction. Monitoring, logging, notifications....</p>
</blockquote>
<div>
- <b>Elliot Alderson</b>
</div>

<h2 id="heading-what-are-daemons">What Are Daemons?</h2>
<p>Pronounced as <code>day-mons</code>. Daemons are processes that run in the background. They are usually used to perform tasks that are not directly related to the user. For example, a daemon might be used to monitor a network connection and log the traffic.</p>
<h2 id="heading-identifying-your-daemons">Identifying Your Daemons</h2>
<p>A simple way of identifying daemons on your linux system is to run the <code>pstree</code> command.</p>
<pre><code class="lang-bash">pstree
</code></pre>
<h2 id="heading-summoning-your-daemons">Summoning Your Daemons</h2>
<p>You can summon your daemons by running the <code>service</code> command.</p>
<pre><code class="lang-bash">service &lt;daemon&gt; start
service &lt;daemon&gt; stop
service &lt;daemon&gt; restart
service &lt;daemon&gt; status
</code></pre>
<p>Want to spin up a daemon?</p>
<pre><code class="lang-bash">sudo systemctl start &lt;daemon&gt;
sudo systemctl stop &lt;daemon&gt;
sudo systemctl restart &lt;daemon&gt;
sudo systemctl status &lt;daemon&gt;
</code></pre>
<p>Finally using systemd you can manage your daemons. A quick boot up daemon example has already been published by me here - <a target="_blank" href="../Hardware/Microprocessor/Raspberry%20Pi%203/Systemd-Reboot.md">Running Scripts On Boot</a>.</p>
<h2 id="heading-final-thoughts">Final Thoughts</h2>
<p>A quick way to identify daemons even includes looking at the suffix of services. If it ends with a <code>d</code> it's a daemon.</p>
<p>Examples:</p>
<ul>
<li><code>mysqld</code></li>
<li><code>systemd</code></li>
<li><code>httpd</code></li>
<li><code>sshd</code></li>
</ul>
]]></content:encoded></item><item><title><![CDATA[Running Scripts On Boot Using Systemd]]></title><description><![CDATA[Sometimes one may wish to run scripts at boot. I looked at a lot of solutions including crontab but the only solution that worked for me was systemd. This can be done for all Linux systems but since I did this on the Raspberry Pi I have included it h...]]></description><link>https://blog.anjann.dev/running-scripts-on-boot-using-systemd</link><guid isPermaLink="true">https://blog.anjann.dev/running-scripts-on-boot-using-systemd</guid><category><![CDATA[automation]]></category><category><![CDATA[Linux]]></category><category><![CDATA[systemd]]></category><dc:creator><![CDATA[Anjan Nair]]></dc:creator><pubDate>Wed, 10 Aug 2022 07:34:14 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1660116624027/qj8Dp5lwi.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>Sometimes one may wish to run scripts at boot. I looked at a lot of solutions including crontab but the only solution that worked for me was <code>systemd</code>. This can be done for all Linux systems but since I did this on the Raspberry Pi I have included it here.</p>
<h2 id="heading-getting-started">Getting started</h2>
<p>We will be creating a <code>.service</code> file in <code>/usr/lib/systemd/system</code>.</p>
<p>This will be followed by <code>sudo nano /etc/systemd/system/discord-webhook.service</code>. In the file you will include the following lines of code</p>
<pre><code class="lang-service">[Unit]
Description=Discord Webhook IP sender

[Install]
WantedBy=multi-user.target

[Service]
ExecStart=python /bin/main.py
Type=simple
User=pi
Group=pi
WorkingDirectory=/bin
Restart=on-failure
</code></pre>
<p>Once done hit <strong>Ctrl+X</strong> followed by <strong>Y</strong> and <strong>Enter</strong></p>
<p>Before actually starting the service you may want to ensure that you have the <code>main.py</code> code. Here is the code (you may change it to your needs)</p>
<p>Run this - <code>sudo nano /bin/main.py</code></p>
<pre><code class="lang-py"><span class="hljs-keyword">import</span> socket
<span class="hljs-keyword">import</span> requests
<span class="hljs-keyword">import</span> urllib.request

webhookURL = <span class="hljs-string">'&lt;webhook url&gt;'</span>
hostname = socket.gethostname()
external_ipv6 = urllib.request.urlopen(<span class="hljs-string">'https://v6.ident.me'</span>).read().decode(<span class="hljs-string">'utf8'</span>)
external_ipv4 = urllib.request.urlopen(<span class="hljs-string">'https://v4.ident.me'</span>).read().decode(<span class="hljs-string">'utf8'</span>)


data = {
    <span class="hljs-string">'username'</span>: <span class="hljs-string">'Server IP Webhook'</span>,
    <span class="hljs-string">'embeds'</span>: [{
        <span class="hljs-string">'title'</span>: <span class="hljs-string">'Server started!'</span>,
        <span class="hljs-string">'description'</span>: hostname + <span class="hljs-string">'\n'</span> + <span class="hljs-string">'IPV6'</span> + <span class="hljs-string">': '</span> + <span class="hljs-string">'`'</span> + external_ipv6 + <span class="hljs-string">'`'</span>  + <span class="hljs-string">'\n\n'</span> + <span class="hljs-string">'IPV4 '</span> + <span class="hljs-string">':'</span>  + <span class="hljs-string">'`'</span> + external_ipv4 + <span class="hljs-string">'`'</span>,
    }]
}

result = requests.post(webhookURL, json = data)

<span class="hljs-keyword">try</span>:
    result.raise_for_status()
<span class="hljs-keyword">except</span>:
    <span class="hljs-keyword">pass</span>
</code></pre>
<ol>
<li>Run <code>sudo systemctl daemon-reload</code></li>
<li>Run <code>sudo systemctl start discord-webhook.service</code> (if there is an output then your code is functioning successfully!)</li>
<li>Finally <code>sudo systemctl enable discord-webhook.service</code></li>
</ol>
<p>And you're good to go! Your script will now run at startup!</p>
<p>Credits - <a target="_blank" href="https://stackoverflow.com/a/38484205">StackOverFlow</a></p>
]]></content:encoded></item><item><title><![CDATA[Secure Copy over SSH]]></title><description><![CDATA[To transfer files between the Raspberry Pi and the host device or vice versa we use a simple command called scp. Now I like to use a headless Raspbian OS without a GUI on the Raspberry Pi as it saves resources which would be otherwise spent unnecessa...]]></description><link>https://blog.anjann.dev/secure-copy-over-ssh</link><guid isPermaLink="true">https://blog.anjann.dev/secure-copy-over-ssh</guid><category><![CDATA[ssh]]></category><category><![CDATA[scp]]></category><category><![CDATA[terminal]]></category><category><![CDATA[cli]]></category><dc:creator><![CDATA[Anjan Nair]]></dc:creator><pubDate>Wed, 10 Aug 2022 07:28:26 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1660116329703/eb6YhjS0z.jpg" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>To transfer files between the Raspberry Pi and the host device or vice versa we use a simple command called <code>scp</code>. Now I like to use a headless Raspbian OS without a GUI on the Raspberry Pi as it saves resources which would be otherwise spent unnecessarily.</p>
<h1 id="heading-what-is-scp">What is SCP?</h1>
<p>SCP short for Secure Copy uses SSH (secure shell) to transfer files between 2 devices. In my experience I hae tested this out with movies as big as 3GB. Although yes it does take some time it has been one of the easiest and convenient method for me!</p>
<h1 id="heading-syntax">Syntax</h1>
<pre><code class="lang-shell">scp example.mp4 pi@192.168.1.1:/home/pi/Movies
</code></pre>
<p>For downloading files from the other computer/server to the local machine</p>
<pre><code class="lang-shell">scp username@192.168.1.1:path_to_file path_in_the_local_machine
</code></pre>
<p>You have to replace <code>example.mp4</code> with the file you intend to copy over, replace the <code>pi@192.168.1.1</code> with your SSH address and <code>/home/pi/Movies</code> with the path you want your movie to be in.</p>
<h2 id="heading-additional-points">Additional Points</h2>
<p>1) If you are sending a folder don't forget to add a <code>-r</code> (recursive tag)
2) You can send multiple files/folders just by adding them one after another (with space) after <code>example.mp4</code></p>
]]></content:encoded></item><item><title><![CDATA[Grafana Dashboard Monitoring System]]></title><description><![CDATA[One of the interesting things one can do with their RaspberryPi is setting up a monitoring dashboard. For this we use Grafana (a dashboard service), InfluxDB (the database) and collectd (a metric collection service).
Updating and Upgrading
Down to yo...]]></description><link>https://blog.anjann.dev/grafana-dashboard-monitoring-system</link><guid isPermaLink="true">https://blog.anjann.dev/grafana-dashboard-monitoring-system</guid><category><![CDATA[Grafana]]></category><category><![CDATA[Raspberry Pi]]></category><dc:creator><![CDATA[Anjan Nair]]></dc:creator><pubDate>Wed, 10 Aug 2022 07:24:28 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1660116090556/yySL8v-X_.jpg" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>One of the interesting things one can do with their RaspberryPi is setting up a monitoring dashboard. For this we use <strong>Grafana</strong> (a dashboard service), <strong>InfluxDB</strong> (the database) and <strong>collectd</strong> (a metric collection service).</p>
<h2 id="heading-updating-and-upgrading">Updating and Upgrading</h2>
<p>Down to your basics, update and upgrade your system first</p>
<pre><code class="lang-bash">sudo apt update &amp;&amp; sudo apt upgrade
</code></pre>
<h2 id="heading-influxdb">InfluxDB</h2>
<p>Installing InfluxDB</p>
<pre><code class="lang-bash">sudo apt install influxdb influxdb-client
</code></pre>
<h3 id="heading-fiddling-with-configuration">Fiddling with Configuration</h3>
<pre><code class="lang-bash">sudo nano /etc/influxdb/influxdb.conf
</code></pre>
<p>The values of the directories are going to be changed to <code>tmp</code> so that values are stored in RAM.</p>
<p>End result should look like this</p>
<pre><code class="lang-conf">[meta]
dir = "/tmp/influxdb/meta"

# ABRIDGED

[data]
dir = "/tmp/influxdb/data"
wal-dir = "/tmp/influxdb/wal"
</code></pre>
<p>Now we will bind our data to localhost only.</p>
<pre><code class="lang-conf">[http]
enabled = true
bind-address = "127.0.0.1:8086"
</code></pre>
<p>Changing collectd configuration</p>
<pre><code class="lang-conf">[[collectd]]
enabled = true
port = 25826
database = "collectd_db"
typesdb = "/usr/share/collectd/types.db"
</code></pre>
<p>Done! Now we will run the following code to enable and start InfluxDB</p>
<pre><code class="lang-bash">sudo systemctl <span class="hljs-built_in">enable</span> influxdb
sudo systemctl start influxdb
</code></pre>
<p>If you now run <code>influx</code> in your terminal you should see it work!</p>
<p>Run these commands</p>
<pre><code class="lang-sql"><span class="hljs-keyword">CREATE</span> <span class="hljs-keyword">DATABASE</span> collectd_db
<span class="hljs-keyword">CREATE</span> <span class="hljs-keyword">RETENTION</span> <span class="hljs-keyword">POLICY</span> <span class="hljs-string">"twentyfour_hours"</span> <span class="hljs-keyword">ON</span> <span class="hljs-string">"collectd_db"</span> <span class="hljs-keyword">DURATION</span> <span class="hljs-number">24</span>h <span class="hljs-keyword">REPLICATION</span> <span class="hljs-number">1</span> <span class="hljs-keyword">DEFAULT</span>
</code></pre>
<p>"<strong>twentyfour_hours</strong> is the name of the policy, should you want to delete it later. the <strong>REPLICATION</strong> directive is only relevant for clustered systems but must be set nonetheless. Here, we only want one copy of our data.</p>
<p>Press <strong>CTRL + D</strong> to exit the influx prompt, and start with the next step."</p>
<h2 id="heading-collectd">collectd</h2>
<p>Installing collectd - </p>
<pre><code class="lang-bash">sudo apt install collectd collectd-utils
</code></pre>
<p>Once again, open the configuration file <code>/etc/collectd/collectd.conf</code> and ensure the following settings are not commented out:
(Usually they aren't commented out but just check)</p>
<pre><code>Hostname "microserver314"

<span class="hljs-type">Interval</span> <span class="hljs-number">60</span>
LoadPlugin syslog
LoadPlugin cpu
LoadPlugin cpufreq
LoadPlugin df
LoadPlugin disk
LoadPlugin entropy
LoadPlugin interface
LoadPlugin irq
LoadPlugin <span class="hljs-keyword">load</span>
LoadPlugin memory
LoadPlugin network
LoadPlugin processes
LoadPlugin swap
LoadPlugin thermal
LoadPlugin users
</code></pre><p>What does all this mean??
The individual plugin report:</p>
<ol>
<li>cpufreq: the CPU frequency</li>
<li>df: available disk space</li>
<li>entropy: available entropy for (pseudo) random number generation</li>
<li>interface: transmitted and received bytes on the network interfaces</li>
<li>irq: number of times the interrupt handler of the OS has been called</li>
<li>load: CPU load averages</li>
<li>memory: available and used main memory</li>
<li>network: write data to network servers</li>
<li>processes: number of processes and their state</li>
<li>swap: size and usage of the swap partition</li>
<li>thermal: CPU temperature</li>
<li>users: number of logged in users (via SSH, …)</li>
</ol>
<p>Now that the plugins are loaded, we must configure some of them individually:</p>
<pre><code><span class="hljs-section">&lt;Plugin df&gt;</span>
<span class="hljs-comment"># This will ignore uninteresting file systems</span>
<span class="hljs-comment"># to keep our DB from cluttering</span>
    <span class="hljs-attribute">FSType</span> rootfs
    <span class="hljs-attribute">FSType</span> sysfs
    <span class="hljs-attribute">FSType</span> proc
    <span class="hljs-attribute">FSType</span> devpts
    <span class="hljs-attribute">FSType</span> tmpfs
    <span class="hljs-attribute">FSType</span> fusectl
    <span class="hljs-attribute">FSType</span> cgroup
    <span class="hljs-attribute">Ignore</span> Selected true
<span class="hljs-section">&lt;/Plugin&gt;</span>

<span class="hljs-section">&lt;Plugin <span class="hljs-string">"syslog"</span>&gt;</span>
<span class="hljs-comment"># Skip messages with info label</span>
    <span class="hljs-attribute">LogLevel</span> <span class="hljs-string">"warning"</span>
<span class="hljs-section">&lt;/Plugin&gt;</span>
</code></pre><p>Finally, we must tell collectd where to write all the data. We will set it to the host and port of InfluxDB’s collectd listener:</p>
<pre><code><span class="hljs-operator">&lt;</span>Plugin <span class="hljs-string">"network"</span><span class="hljs-operator">&gt;</span>
    Server <span class="hljs-string">"127.0.0.1"</span> <span class="hljs-string">"25826"</span>
<span class="hljs-operator">&lt;</span><span class="hljs-operator">/</span>Plugin<span class="hljs-operator">&gt;</span>
</code></pre><p>Finally making it work!</p>
<pre><code class="lang-bash">sudo systemctl <span class="hljs-built_in">enable</span> collectd
sudo systemctl start collectd
</code></pre>
<h3 id="heading-testing-the-data">Testing The Data</h3>
<pre><code class="lang-bash">influxdb
</code></pre>
<pre><code class="lang-sql"><span class="hljs-keyword">USE</span> collectd_db
<span class="hljs-keyword">SELECT</span> * <span class="hljs-keyword">FROM</span> processes_value <span class="hljs-keyword">WHERE</span> (<span class="hljs-string">"type_instance"</span> = <span class="hljs-string">'sleeping'</span>) <span class="hljs-keyword">ORDER</span> <span class="hljs-keyword">BY</span> <span class="hljs-built_in">time</span> <span class="hljs-keyword">DESC</span> <span class="hljs-keyword">LIMIT</span> <span class="hljs-number">1</span>
</code></pre>
<p>If you get an output you are good to go!</p>
<h2 id="heading-grafana">Grafana</h2>
<p>Finally we install the dashboard
To add the Grafana APT key to your Raspberry Pi’s keychain, run the following command</p>
<pre><code class="lang-bash">curl https://packages.grafana.com/gpg.key | gpg --dearmor | sudo tee /usr/share/keyrings/grafana-archive-keyrings.gpg &gt;/dev/null
</code></pre>
<p>With the key added, we can now safely add the Grafana repository to our Pi’s list of packages sources.</p>
<p>Use the following command on your Raspberry Pi to add the repository to the list.</p>
<pre><code class="lang-bash"><span class="hljs-built_in">echo</span> <span class="hljs-string">"deb [signed-by=/usr/share/keyrings/grafana-archive-keyrings.gpg] https://packages.grafana.com/oss/deb stable main"</span> | sudo tee /etc/apt/sources.list.d/grafana.list
</code></pre>
<p>Finally updating our Pi</p>
<pre><code class="lang-bash">sudo apt update
</code></pre>
<p>And installing it</p>
<pre><code class="lang-bash">sudo apt install grafana
</code></pre>
<p>Enabling and starting the Grafana service</p>
<pre><code class="lang-bash">sudo systemctl <span class="hljs-built_in">enable</span> grafana-server
sudo systemctl start grafana-server
</code></pre>
<p>Now open Grafana using your host IP address and on port <em>3000</em>. The password and username for the dashboard is <strong>admin</strong>.</p>
<p>Note - Do not like port 3000 or another service is utilizing it? Change the port in <code>/etc/grafana/grafana.ini</code> you have to change in <code>/usr/share/grafana/conf/defaults.ini</code>.</p>
<p>Remove the semicolon in <code>/usr/share/grafana/conf/defaults.ini</code> and chance the port! You're good to go!</p>
<h1 id="heading-finally">Finally</h1>
<ol>
<li>Open Grafana and if not prompted, head to settings (the gear icon) and click on <strong>Data Sources</strong></li>
<li>Add the data source as InfluxDB as given below</li>
</ol>
<p><img src="https://cdn.discordapp.com/attachments/456482075307016192/965186519646216253/unknown.png" alt="Top Page" />
<img src="https://cdn.discordapp.com/attachments/456482075307016192/965192157235384340/unknown.png" alt="Bottom Page" /></p>
<p>Done! Now just save and test! If it works you are 95% done!</p>
<h2 id="heading-getting-the-beautiful-output">Getting the beautiful output</h2>
<p>Head over to the <strong>+</strong> sign and <strong>Import</strong> <a target="_blank" href="https://ch-st.de/assets/posts/raspberry-pi-grafana-influxdb-collectd/dashboard.json">this</a> and you shall now be seeing an output!</p>
]]></content:encoded></item><item><title><![CDATA[PiVPN - Raspberry Pi VPN]]></title><description><![CDATA[PiVPN is a self hosted VPN service one can install on the Raspberry Pi. This service is light and serves both OpenVPN or Wireguard depending on the user's choice. This tutorial will show the steps in installing PiVPN with a Wireguard configuration.
H...]]></description><link>https://blog.anjann.dev/pivpn</link><guid isPermaLink="true">https://blog.anjann.dev/pivpn</guid><category><![CDATA[vpn]]></category><category><![CDATA[Raspberry Pi]]></category><category><![CDATA[pivpn]]></category><dc:creator><![CDATA[Anjan Nair]]></dc:creator><pubDate>Wed, 10 Aug 2022 07:19:20 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1660115780222/wQRNH9rvA.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>PiVPN is a <strong>self hosted VPN service</strong> one can install on the Raspberry Pi. This service is light and serves both OpenVPN or Wireguard depending on the user's choice. This tutorial will show the steps in installing PiVPN with a <strong>Wireguard</strong> configuration.</p>
<h2 id="heading-how-is-this-different-from-any-other-tutorial">How is this different from any other tutorial?</h2>
<p>The initial setup is no different from all other references but here we use Wireguard along with <strong>IPv6</strong> to setup the server. Most of the times one cannot get a static IPv4 address to setup a PiVPN, IPv6 fixes that issue for these cases as 99% of the time it remains static. Incase your ISP changes your IPv6, well, its time to switch to a different one.</p>
<p>Yes some may say one can use a DDNS service but honestly its up-to you. My ISP did not allow me to port forward on my IPv4 address but IPv6 had no such restrictions. Hence this tutorial covers IPv6.</p>
<h2 id="heading-installing-the-software">Installing the software</h2>
<p><strong>A small pre-requisite</strong> : Already have <a target="_blank" href="https://pi-hole.net/">Pi-hole</a> installed as you can pass all your queries through the Pi-hole.</p>
<blockquote>
<p>Instead of browser plugins or other software on each computer, install Pi-hole in one place and your entire network is protected.</p>
</blockquote>
<p>Once Pi-hole is setup all you have to do is run the script to install PiVPN</p>
<pre><code class="lang-bash">curl -L https://install.pivpn.io | bash
</code></pre>
<p>An automatic installation of PiVPN should take place and just go through with the installation GUI. There will be one part where you will be asked to input your <strong>static IPv4 address</strong>. In that just hit enter and move on, we will be making changes to that later!</p>
<p>After it is installed, in your terminal with your favorite editor (nano or vim) edit the Wireguard configuration file -</p>
<pre><code class="lang-bash">sudo nano /etc/pivpn/wireguard/setupVars.conf
</code></pre>
<p>Your end result should be something like this -</p>
<pre><code class="lang-bash">PLAT=Debian
OSCN=bullseye
USING_UFW=0
IPv4dev=wlan0
IPv6dev=wlan0
install_user=root
install_home=/home/root
VPN=wireguard
pivpnPORT=51820
pivpnDNS1=some.dns.com
pivpnDNS2=
pivpnHOST=<span class="hljs-string">"[YOUR IPV6 ADDRESS]"</span>
INPUT_CHAIN_EDITED=0
FORWARD_CHAIN_EDITED=0
INPUT_CHAIN_EDITEDv6=0
FORWARD_CHAIN_EDITEDv6=0
pivpnPROTO=udp
pivpnMTU=1420
pivpnDEV=wg0
pivpnNET=10.53.171.0
subnetClass=24
pivpnforceipv6route=1
pivpnforceipv6=1
pivpnenableipv6=1
pivpnNETv6=<span class="hljs-string">"fd11:5ee:bad:c0de::"</span>
subnetClassv6=64
ALLOWED_IPS=<span class="hljs-string">"0.0.0.0/0, ::0/0"</span>
UNATTUPG=1
INSTALLED_PACKAGES=(grepcidr bsdmainutils iptables-persistent wireguard-tools qrencode unattended-upgrades)
</code></pre>
<h3 id="heading-a-few-tips">A Few Tips</h3>
<ul>
<li>In the above options put your IPV6 (in <em>pivpnHOST</em>) inside the square brackets for it to work.</li>
<li>The <em>pivpnDNS1</em> should be automatically configured to your pi-hole address, if not, then manually configure it.</li>
<li>Edit <em>install_user</em> and <em>install_home</em> to your user and home directory respectively.</li>
<li><strong>Mainly</strong> ensure <em>pivpnforceipv6</em> and <em>pivpnenableipv6</em> are set to 1.</li>
</ul>
<p>Now just run</p>
<pre><code class="lang-bash">pivpn -add
</code></pre>
<p>to add your client to the list of devices</p>
<p>AND</p>
<pre><code class="lang-bash">pivpn -qr
</code></pre>
<p>To generate a QR code for mobile devices</p>
<p>Your PiVPN should be good to go! Make sure to make changes to your UFW configurations to allow UDP traffic through the inputted port (by default is 51820).</p>
]]></content:encoded></item><item><title><![CDATA[Pi-hole with Raspberry Pi 3]]></title><description><![CDATA[A pi-hole is basically a black-hole for internet ads. The project is an open source ad blocking software at the DNS level.
Installing Pi-hole
The easiest way of installing Pi-hole is by running the following script
curl -sSL https://install.pi-hole.n...]]></description><link>https://blog.anjann.dev/pi-hole</link><guid isPermaLink="true">https://blog.anjann.dev/pi-hole</guid><category><![CDATA[Raspberry Pi]]></category><category><![CDATA[adblock]]></category><category><![CDATA[Pi-Hole]]></category><dc:creator><![CDATA[Anjan Nair]]></dc:creator><pubDate>Wed, 10 Aug 2022 07:13:26 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1660115435359/gBeOoyTvS.webp" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>A pi-hole is basically a black-hole for internet ads. The project is an open source ad blocking software at the DNS level.</p>
<h2 id="heading-installing-pi-hole">Installing Pi-hole</h2>
<p>The easiest way of installing Pi-hole is by running the following script</p>
<pre><code class="lang-bash">curl -sSL https://install.pi-hole.net | bash
</code></pre>
<p>The script will do everything to install the software and convert your Raspberry Pi into a pi-hole.</p>
<p>Once done, you can open the web interface of the pi-hole. The web interface is simple to understand and easy to setup too.</p>
<p>Finally before moving on we set the DNS of our router to the IP address of our pi-hole.</p>
<h2 id="heading-ad-lists">Ad-lists</h2>
<p>To add lists apart from the one used at setup we go to <em>Group Management</em>&gt;<em>Adlists</em></p>
<p>My preferred lists are - </p>
<p>1) Normal Hosts - https://v.firebog.net/hosts/static/w3kbl.txt</p>
<p>2) Adguard List - https://v.firebog.net/hosts/AdguardDNS.txt</p>
<p>3) EasyList - https://v.firebog.net/hosts/Easylist.txt</p>
<p>4) Privacy - https://v.firebog.net/hosts/Easyprivacy.txt</p>
<p>5) Windows Spyware - https://raw.githubusercontent.com/crazy-max/WindowsSpyBlocker/master/data/hosts/spy.txt</p>
<p>6) Anti-Malware List -     https://raw.githubusercontent.com/DandelionSprout/adfilt/master/Alternate%20versions%20Anti-Malware%20List/AntiMalwareHosts.txt</p>
<p>7) Threat Intel by OSINT - https://osint.digitalside.it/Threat-Intel/lists/latestdomains.txt</p>
<p>8) Adult sites - https://blocklistproject.github.io/Lists/porn.txt</p>
<p>You can add all this together by adding the URLs separated by space. After doing so make sure to update the <em>Gravity</em> list by running <code>pihole -g</code> command or by heading to <em>Tools</em> and <em>Update Gravity</em>. This downloads the list and updates it.</p>
<h2 id="heading-dns-over-https-doh">DNS over HTTPS (DOH)</h2>
<p>To setup DNS over HTTPS on your pi-hole we use the docs given in the official website <a target="_blank" href="https://docs.pi-hole.net/guides/dns/cloudflared/">here</a>.</p>
<p><strong>Note</strong> - Go with the manual method as the automatic did not work for me.</p>
]]></content:encoded></item><item><title><![CDATA[NGINX Reverse Proxy]]></title><description><![CDATA[An NGINX reverse proxy comes in use when you need to serve multiple local servers on different ports (remember the localhost:8080?). All you require is a small software called nginx.
What will be covered here?
Although the title talks about setting u...]]></description><link>https://blog.anjann.dev/nginx-reverse-proxy</link><guid isPermaLink="true">https://blog.anjann.dev/nginx-reverse-proxy</guid><category><![CDATA[nginx]]></category><category><![CDATA[Reverse Proxy]]></category><category><![CDATA[server]]></category><dc:creator><![CDATA[Anjan Nair]]></dc:creator><pubDate>Wed, 10 Aug 2022 07:08:28 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1660114907721/FO2liyajQ.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>An NGINX reverse proxy comes in use when you need to serve multiple local servers on different ports (remember the <code>localhost:8080</code>?). All you require is a small software called nginx.</p>
<h3 id="heading-what-will-be-covered-here">What will be covered here?</h3>
<p>Although the title talks about setting up a reverse proxy we will be doing that and along with that setting up Cloudflare DNS as well as Certbot certificate generation.</p>
<h2 id="heading-installations">Installations</h2>
<h3 id="heading-installing-nginx">Installing NGINX</h3>
<p>Basic installation procedure just run the command </p>
<pre><code class="lang-bash">sudo apt-get install nginx
</code></pre>
<p>After installing NGINX just run </p>
<pre><code class="lang-bash">sudo systemctl start nginx
</code></pre>
<p>This should start the NGINX server at port 80. Just input your machine's IP in your browser and a welcome message should be visible.</p>
<h3 id="heading-installing-certbot">Installing Certbot</h3>
<div>
<img src="https://i.imgur.com/K5841WW.png" />
</div>

<p>A controversial method yes, but the only method I found convenient was installing Certbot from <a target="_blank" href="https://certbot.eff.org/lets-encrypt/">here</a> using <em>snap</em>. Alternate methods are present too and you are free to check them out! The link above should be self explanatory.</p>
<h2 id="heading-cloudflare-dns">Cloudflare DNS</h2>
<p>To setup up a Cloudflare DNS for your domain, ensure your domain is linked to your Cloudflare account. This can be done by "verifying" you are the owner of your domain. Once that is done just head over to the <strong>DNS</strong> section as shown below.</p>
<div>
<img src="https://i.imgur.com/8LGmJC9.png" />
</div>

<p>Add a record and make it A or AAAA depending on what IP address you want to use. A is for IPV4 and AAAA is for IPV6. The IP address is your server IP address which your web servers/applications are hosted on.</p>
<div>
<img src="https://i.imgur.com/tq4l1Jp.png" />
</div>

<p>If you do not want to use a DDNS service such as No-Ip you can refer <a target="_blank" href="https://www.youtube.com/watch?v=rI-XxnyWFnM">this</a> video which explains how to use Cloudflare's API and do it automatically.</p>
<h2 id="heading-nginx-configuration">NGINX Configuration</h2>
<p>Here it is considered your certificate is ready, your DNS configured and you have NGINX installed.</p>
<p>A quick check should reveal an existing folder - <code>/etc/nginx/sites-enabled/</code></p>
<p>This folder is where all your configurations for the reverse proxy will be.</p>
<h3 id="heading-creating-a-configuration-file">Creating a configuration file</h3>
<p>Create a file in the folder mentioned above with an extension <code>.conf</code>. For example <code>test.conf</code>.</p>
<p>Now all you need to do is copy paste this configuration -</p>
<pre><code class="lang-conf">server {
   listen 80;
   listen [::]:80;
   server_name test.your.domain;
   return 301 https://test.your.domain$request_uri;
 }

server {
   listen 443 ssl;
   listen [::]:443 ssl;
   server_name test.your.domain;
   ssl_certificate  /etc/letsencrypt/live/test.your.domain/fullchain.pem;
   ssl_certificate_key  /etc/letsencrypt/live/test.your.domain/privkey.pem;
   ssl_trusted_certificate /etc/letsencrypt/live/test.your.domain/chain.pem;
   ssl_prefer_server_ciphers on;

   location / {
        proxy_http_version 1.1;
        proxy_pass http://127.0.0.1:3001/;
        proxy_set_header        Host $host;
        proxy_set_header        X-Real-IP $remote_addr;
        proxy_set_header        X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header        X-Forwarded-Proto $scheme;
        }
}
</code></pre>
<p>Some changes you should be making in the configuration file:</p>
<ul>
<li><code>test.your.domain</code> should be substituted with your domain/subdomain name. The domain/subdomain you input here should be the same as inputted in the Cloudflare DNS section.</li>
<li>Your ssl certificates generated by the Certbot should be in the given folder location. If not change in manually.</li>
<li>If you have a website which accepts files (for example uploading of images and files) add this line - <code>client_max_body_size 0;</code></li>
<li>Inside <code>location /</code> substitute the domain in front of <code>proxy_pass</code> to your required domain and port.</li>
</ul>
<p>To conclude, the same steps can be repeated as many number of times you want to create configurations for different domains/subdomains. All you have to do is create a new <code>conf</code> file.</p>
<p>💡 <strong>Tips</strong></p>
<ul>
<li>There is a really good tool provided by DigitalOcean for generating configuration files: <a target="_blank" href="https://github.com/digitalocean/nginxconfig.io">Check it out</a></li>
<li>If you have another service for example Pi-hole running on port 80 you should switch that over to another port (lighttpd configuration: <code>/etc/lighttpd/lighttpd.conf</code>).</li>
</ul>
]]></content:encoded></item><item><title><![CDATA[Ducky on Raspberry Pi Pico]]></title><description><![CDATA[A Pico Ducky is a USB rubber ducky, defined on the internet as -
"USB Rubber ducky is an HID device that looks similar to a USB Pen drive. It may be used to inject keystroke into a system, used to hack a system, steal victims essential and credential...]]></description><link>https://blog.anjann.dev/ducky-on-raspberry-pi-pico</link><guid isPermaLink="true">https://blog.anjann.dev/ducky-on-raspberry-pi-pico</guid><category><![CDATA[raspberry-pi-pico]]></category><category><![CDATA[ducky]]></category><category><![CDATA[rubber ducky]]></category><dc:creator><![CDATA[Anjan Nair]]></dc:creator><pubDate>Wed, 10 Aug 2022 07:00:04 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/unsplash/yC_7U1g3Kvs/upload/v1660114390896/fXFDrvLNg.jpeg" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>A Pico Ducky is a USB rubber ducky, defined on the internet as -
"USB Rubber ducky is an HID device that looks similar to a USB Pen drive. It may be used to inject keystroke into a system, used to hack a system, steal victims essential and credential data can inject payload to the victim's computers."</p>
<h1 id="heading-getting-started">Getting Started</h1>
<p>If you want to skip to the Github Repository <a target="_blank" href="https://github.com/dbisu/pico-ducky">here</a> is the link.</p>
<ol>
<li><p>Download the <code>.uf2</code> file from <a target="_blank" href="https://circuitpython.org/board/raspberry_pi_pico/">CircuitPython for the Raspberry Pi Pico</a></p>
</li>
<li><p>Plug the device into a USB port while holding the boot button. It will show up as a removable media device named <code>RPI-RP2</code>.</p>
</li>
<li><p>Copy the downloaded <code>.uf2</code> file to the root of the Pico (<code>RPI-RP2</code>). The device will reboot and after a second or so, it will reconnect as <code>CIRCUITPY</code>.</p>
</li>
<li><p>Download <code>adafruit-circuitpython-bundle-7.x-mpy-YYYYMMDD.zip</code> from <a target="_blank" href="https://github.com/adafruit/Adafruit_CircuitPython_Bundle/releases/latest">here</a> and extract it on your main device (not the Pico).</p>
</li>
<li><p>Navigate to <code>lib</code> in the recently extracted folder and copy <code>adafruit_hid</code> to the <code>lib</code> folder in your Raspberry Pi Pico.</p>
</li>
<li><p>Click <a target="_blank" href="https://raw.githubusercontent.com/dbisu/pico-ducky/main/duckyinpython.py">here</a>, press CTRL + S and save the file as <code>code.py</code> in the root of the Raspberry Pi Pico, overwriting the previous file.</p>
</li>
<li><p>Before the next step, let us enter setup mode. To enter setup mode by connecting the pin 1 (<code>GP0</code>) to pin 3 (<code>GND</code>), this will stop the pico-ducky from injecting the payload in your own machine.
The easiest way to so is by using a jumper wire between those pins as seen bellow.</p>
</li>
</ol>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1660114671231/qeoY5YupT.png" alt="setup-mode.png" /></p>
<ol>
<li><p>Find a script <a target="_blank" href="https://github.com/hak5darren/USB-Rubber-Ducky/wiki/Payloads">here</a> or <a target="_blank" href="https://github.com/hak5darren/USB-Rubber-Ducky/wiki/Duckyscript">create your own one using Ducky Script</a> and save it as <code>payload.dd</code> in the Pico.</p>
</li>
<li><p>Be careful, if your device isn't in <a class="post-section-overview" href="#setup-mode">setup mode</a>, the device will reboot and after half a second, the script will run.</p>
</li>
</ol>
<h2 id="heading-enable-disable-mode">Enable Disable Mode</h2>
<p>If you need the pico-ducky to not show up as a USB mass storage device for stealth, follow these instructions.</p>
<ol>
<li>Enter setup mode.</li>
<li>Copy boot.py to the root of the pico-ducky.</li>
<li>Copy your payload script to the pico-ducky.</li>
<li>Disconnect the pico from your host PC.</li>
<li>Connect a jumper wire between pin 18 (<code>GND</code>) and pin 20 (<code>GPIO15</code>).</li>
<li>This will prevent the pico-ducky from showing up as a USB drive when plugged into the target computer. </li>
<li>Remove the jumper and reconnect to your PC to reprogram.</li>
<li>The default mode is USB mass storage enabled.</li>
</ol>
]]></content:encoded></item></channel></rss>