<?xml version="1.0" encoding="utf-8"?>
<feed xmlns="http://www.w3.org/2005/Atom">
  <title>Cosmic Bytes</title>
  <subtitle></subtitle>
  <link href="https://cosmicbyt.es/feed.xml" rel="self"/>
  <link href="https://cosmicbyt.es/"/>
  
    <updated>2024-07-04T00:00:00Z</updated>
  
  <id>https://cosmicbyt.es</id>
  <author>
    <name>Nezar Tarbin</name>
    <email>nizar.t96@gmail.com</email>
  </author>
  
    
    <entry>
      <title>RSYNC as a Backup Server: Simple, Powerful and Flexible</title>
      <link href="https://cosmicbyt.es/posts/rsync-backup-server/"/>
      <updated>2023-02-13T00:00:00Z</updated>
      <id>https://cosmicbyt.es/posts/rsync-backup-server/</id>
      <content type="html">
        <![CDATA[
      <h2 id="summary" tabindex="-1">Summary</h2>
<p>I will talk about using RSYNC for backing up files (notes, media, etc) to a server from multiple devices. I will talk about its benefits, how to do it, and examples of its extensibility and flexibility.</p>
<h2 id="pre-requisites" tabindex="-1">Pre-requisites</h2>
<p>No pre-requisites to read and understand this article. However, to actually implement this, you do need some kind of self-hosted server, and it is preferred you have some familiarity with the command-line or basic system administration (but you can probably get away without it if you are determined).</p>
<h2 id="introduction%3A-why-have-a-backup-server%3F" tabindex="-1">Introduction: Why Have a Backup Server?</h2>
<p>With our increasing reliance on digital data, resiliency against accidental or malicious loss has become increasingly important. No one likes to lose their photos full of memories that they hold dear!</p>
<p>Saving your data on just a single device (for example, your smartphone) without backups puts you at risk of losing this data. This can happen for a number of reasons:</p>
<ul>
<li>hardware failure</li>
<li>losing the device</li>
<li>disasters (electric surge, fires or floods).</li>
</ul>
<p><strong>The Solution?</strong> Backing up your data and replicating it to another device!</p>
<p>Now, my post is not talking about a typical backup solution. Typical backup solutions are usually:</p>
<ul>
<li>Third party services: Google Drive, Apple iCloud, Dropbox, etc.</li>
<li>full-featured self-hosted services: <a href="https://github.com/nextcloud/server">NextCloud</a>,  <a href="https://github.com/haiwen/seafile">SeaFile</a></li>
</ul>
<p>Instead we will talk about another method using RSYNC, which is a self-hosted solution granting you many benefits in flexibility, customization, and increased control, at the expense of being more DIY solution. We will explore that in detail shortly!</p>
<p>With that out of the way, let's talk more about RSYNC.</p>
<h2 id="what-is-rsync%3F" tabindex="-1">What is RSYNC?</h2>
<h3 id="basic-usage" tabindex="-1">Basic Usage</h3>
<p>RSYNC is &quot;a fast, versatile, remote (and local) file-copying tool&quot; according to its manual page.</p>
<p>In the most simple case, rsync is similar to the <code>cp</code> unix utility: it can copy a file from one path to another.</p>
<pre><code>rsync /path/to/file /path/to/destination
</code></pre>
<h3 id="remote-transfer-capability" tabindex="-1">Remote Transfer Capability</h3>
<p>One of rsync's unique capabilities is its ability to copy or transfer files over a network connection.</p>
<p>This is crucial for our backup system, since we will be backing up files to a separate device, and one of the best way to do this is network transfer.</p>
<p>rsync is required to be installed on both devices.</p>
<p>There are two ways rsync can transfer files remotely:</p>
<ol>
<li>One device must be running the rsync daemon, and the second device runs the rsync command</li>
<li>One device runs the rsync command and uses SSH for the connection. The only requirement is that the second device has rsync installed and ssh working.</li>
</ol>
<p>For more information and details on how to transfer files remotely, please reference rsync's manual page or the many resources on the internet going over this.</p>
<h3 id="rsync-replicates-filesystem-state" tabindex="-1">RSYNC Replicates Filesystem State</h3>
<p>Although basic usage shows rsync to act very much like <code>cp</code>, it can be a lot more powerful. I found it very helpful to think of rsync as a tool to replicate the filesystem state (or part of said state) between one path and another (on one or two different devices) rather than just a basic copying tool. This is different than the working mechanism of the <code>cp</code> utility. This may not make sense yet, so let me demonstrate.</p>
<p>Suppose I have two different paths. One path contains a set of files, the other contains another set of files, and but there are some similarities:</p>
<pre><code>ls /path/to/dir1
#output:
file1 file2

ls /path/to/dir2
#output:
file2 file3
</code></pre>
<p>as we can see, file2 is present in both directories, while file1 is only present in dir1, whereas file3 is only present in dir2</p>
<p>If we use the cp command such as in:</p>
<pre><code>cp --recursive /path/to/dir1/. /path/to/dir2/

ls /path/to/dir2
# output
file1 file2 file3
</code></pre>
<p>the cp command copies all files in dir1 to dir2, and any similar files are overwritten.</p>
<p>Here is what rsync could have done instead:</p>
<ol>
<li><strong>Only copy files from dir1 that are not in dir2 (no overwriting).</strong> Ignore any files that are present in both directories (even if content is different!)</li>
<li><strong>Only copy files from dir1 that are not in dir2.</strong> For files that are present in both directories, only copy it over if the content is different. You can be way more granular than this, such that it is only copied over if the edit timestamp is more recent, for example.</li>
<li><strong>Delete files in dir2 if they are not present in dir1.</strong></li>
</ol>
<p>This is part of what I mean by &quot;replicating state&quot; of a filesystem. It is not only copying files, it is defining a specific state that the filesystem should be in.</p>
<p>And there are many more options! (you can find them all in the rsync manual page). You can ask rsync to copy and preserve file ownership, edit timestamps, file permissions, or you can ask it to change file permissions. RSYNC grants you very fine-grained control over the process, and the possibilities are endless!</p>
<p>I did not go too in-depth here, as my goal is only to quickly showcase the powers of RSYNC and convince you it is fit for this job. There are many resources on using RSYNC out there, including its detailed manual page, so I urge you to check them out.</p>
<h3 id="rsync's-diffing-algorithm-and-network-performance" tabindex="-1">RSYNC's Diffing Algorithm and Network Performance</h3>
<p>As we saw in the previous section, RSYNC is able to skip over copying certain files based on certain conditions (they are already present in the destination path, or their content is similar, or it has not been edited since last sync, etc).</p>
<p>RSYNC utilizes a diffing algorithm to achieve this that is very fast (and configurable). This is very useful for our use case. Suppose you have a photo gallery on your smartphone that you want to backup and sync to your backup server. If every time you ran a backup, it copies EVERY FILE over the network, it will take a while and that is not efficient. RSYNC can instead only copy over files that are not already present on the server. This way, it transfers much less files, and the process is much faster.</p>
<p>Given RSYNC has fairly low overhead, this makes the transfers quite fast.</p>
<h2 id="why-rsync%3F-why-not-nextcloud-or-other-self-hosted-solutions%3F" tabindex="-1">Why RSYNC? Why not NextCloud or Other Self-Hosted Solutions?</h2>
<p>RSYNC is not the best option for everyone, and other alternatives may very well be a more appropriate option for you.</p>
<h3 id="reasons-not-to-use-rsync" tabindex="-1">Reasons not to use RSYNC</h3>
<ul>
<li>it is a backup solution only. It cannot help you browse your backed up files, delete them, share them, etc.</li>
<li>more DIY than alternatives (if you don't like DIY)</li>
<li>Primarily CLI-based (if you don't like CLIs)</li>
<li>can lead to issues if mis-configured</li>
<li>By default, may not have a feature you want, but its customizability makes any missing feature easier to DIY</li>
</ul>
<p>It is important to note that for many people, those are pros, not cons.</p>
<h3 id="why-rsync%3F" tabindex="-1">Why RSYNC?</h3>
<ul>
<li>Focuses on one task and optimizes it really well (Unix philosophy)</li>
<li>fast and performant</li>
<li>versatile, flexible, and rich with features and CLI options</li>
<li>Built-in support for SSH for traffic encryption and remote user authentication</li>
<li>widely available with minimal requirements. Pre-installed on many linux distributions!</li>
<li>has a diffing algorithm, allowing to only transfer files that aren't already transferred</li>
<li>the directory structure of transferred files is under user's control</li>
</ul>
<h2 id="why-not-syncthing%3F" tabindex="-1">Why not SyncThing?</h2>
<p><a href="https://github.com/syncthing/syncthing">Syncthing</a> is an open source remote file sync program. Syncthing can run on multiple devices and synchronize files in one or several directories across several devices (no need for a centralized server).</p>
<p>Syncthing is great! It is not wrong to use Syncthing, it all depends on your needs. While it has a lot of overlap with rsync pertaining to our use case, there are some subtle differences:<br>
<br /></p>
<ul>
<li>Syncthing is replicating the exact state of a directory between different devices. RSYNC grants you fine-grained control on what to copy, as discussed in more detail earlier</li>
<li>With Syncthing, deleting a file on one device deletes them in all synced devices. RSYNC has that behavior as optional, so you can prevent accidental deletions.</li>
<li>Syncthing is encrypted using TLS, and has strong authentication. RSYNC can optionally be encrypted and authenticated with SSH, but otherwise needs a custom solution for encryption and authentication.</li>
<li>Syncthing has a lot of features, such as version control, that RSYNC does not have included and would need a separate program for.</li>
<li>RSYNC is much more scriptable and interoperable with other Linux programs and utilities.</li>
</ul>
<p>Now let's get down to business and implement our RSYNC backup system!</p>
<h2 id="basics-architecture-for-rsync-backups" tabindex="-1">Basics Architecture for RSYNC Backups</h2>
<p>Here is what we need:</p>
<ul>
<li>a server with rsync installed (and either ssh daemon or rsync daemon running)</li>
<li>a device with data it wants to backup, and rsync installed</li>
<li>a network connecting both devices (this could be your home wifi)</li>
<li>invoke rsync to backup! (or set it up to invoke automatically)</li>
<li>... profit?</li>
</ul>
<p>Wait, is it really that simple? that's it?
yeah! Well, kinda ... This is more of a basic (but working!) example. It is quite sufficient, but also leaves you a lot of room to add onto it. First, we will  follow those steps and setup something basic, then explore some options for customizations or add-ons (though I can't go through all of them, because the possibilities are endless!)</p>
<h2 id="installing-rsync" tabindex="-1">Installing RSYNC</h2>
<h3 id="linux" tabindex="-1">Linux</h3>
<p>If you are using Linux, there is a decent chance rsync is already installed. Just try:</p>
<pre><code>rsync --version
</code></pre>
<p>if it does not error, you're in luck!</p>
<p>Otherwise, you can install it using your distribution's package manager. Some examples below:</p>
<pre><code># Debian, Ubuntu and some of their derivatives
sudo apt install rsync

# Arch and derivatives
sudo pacman -S rsync

# Gentoo
emerge rsync
</code></pre>
<p>I bet it is possible to install it with other means. You can probably find resources on this on the internet.</p>
<h3 id="android-using-syncopoli" tabindex="-1">Android using Syncopoli</h3>
<p>There are two ways I know to run rsync on Android: Syncopoli and Termux.</p>
<p><a href="https://gitlab.com/fengshaun/syncopoli">Syncopoli</a> is a an android native client for the rsync protocol.</p>
<ul>
<li>Has a GUI</li>
<li>can schedule the triggering of the rsync job</li>
<li>has most of rsync's features, but is not one-to-one with native rsync, so some features (most of them are uncommon) are not available.</li>
</ul>
<h3 id="android-using-termux" tabindex="-1">Android using Termux</h3>
<p>The second way to use rsync on android is using <a href="https://github.com/termux/termux-app">Termux</a>.</p>
<p>Termux is amazing. It is a terminal emulator for Android, and allows you to explore the world of the Linux terminal from your android device, and this includes rsync.</p>
<ul>
<li>CLI instead of GUI. This means it requires more keyboard, but that can be automated.</li>
<li>Access to many other linux programs and utilities, and can easily chain rsync with them.</li>
<li>Can be scheduled with crontab and tasker</li>
<li>You get the same rsync as the one on Linux with all its features</li>
</ul>
<h3 id="other" tabindex="-1">Other</h3>
<p>You can probably find installation guides for other platforms. I will not include them here. The android one is a bit difficult to find, so I thought it would be good to include here.</p>
<h2 id="configuring-server-side-rsync" tabindex="-1">Configuring Server-Side RSYNC</h2>
<p>There are two basic ways to setup RSYNC on the server side:</p>
<ul>
<li>Server must have SSH Daemon setup and be SSH-able</li>
<li>Server must have rsync daemon running</li>
</ul>
<p>Both methods have pros and cons.</p>
<h4 id="ssh" tabindex="-1">SSH</h4>
<ul>
<li>SSH grants you SSH encryption and authentication by default for the rsync, which is a big security benefit.</li>
<li>SSH requires the client have access to a user that has shell access. Depending on your security model, this may be okay, or may be less secure.</li>
<li>SSH may be slower due to the encryption</li>
<li>requires no rsync-specific setup. If ssh is working and rsync is installed, clients can begin using immediately.</li>
</ul>
<h4 id="rsync-daemon" tabindex="-1">RSYNC Daemon</h4>
<ul>
<li>Has many features that are otherwise unavailable (such as chroot)</li>
<li>does not require shell access, which can be a security benefit depending on security model</li>
<li>transfers unencrypted. This can be mitigated in many ways, such as with secure tunnels (SSH tunnel, TLS tunnel, etc), but that is a more complex setup</li>
<li>weak authentication with weakly encrypted password exchange. Can also be mitigated like above</li>
<li>requires a dedicated daemon to be running in the background.</li>
</ul>
<p>Setting up SSH is out of scope for this post. Many readers have it setup already, and there are many guides on the internet to do it.</p>
<h3 id="setting-up-rsync-daemon" tabindex="-1">Setting up RSYNC Daemon</h3>
<p>To setup the rsync daemon, you first need to setup your <code>rsyncd.conf</code> file to configure the rsync daemon.</p>
<p>Here is an example <code>rsyncd.conf</code> file with some descriptive comments:</p>
<pre><code>chroot = false

[modulename]
path=/path/to/destination
read only = false

[module2]
...
</code></pre>
<p>In the example above, [modulename] defines a module that clients can interact with. You can have multiple modules. a module is usually associated with one path for clients to rsync with, and a specified set of rules (such as our <code>read only = false</code>). Any rules defined under the <code>[modulename]</code> only apply to that module. Rules that are defined above all the modules apply to all the modules (such as our <code>chroot</code>).</p>
<p>I highly recommend exploring the manual page for rsync and rsyncd.conf. There are many options that you will find useful.</p>
<p>Before jumping to the next section, make sure you create the directory where the client will sync data to, if it does not exist already.</p>
<h2 id="invoking-from-client-side-(linux-or-termux)" tabindex="-1">Invoking from Client Side (Linux or Termux)</h2>
<p>Now that we have rsync installed, we can invoke it!</p>
<p>If your server is using SSH, then we can invoke rsync like:</p>
<pre><code>rsync [options] /path/to/local/directory remoteuser@hostname:/path/to/remote/directory
</code></pre>
<p>If your server is using rsync daemon, you can instead do:</p>
<pre><code>rsync [options] /path/to/local/directory rsync://hostname:port/modulename
</code></pre>
<p>There are other ways of invoking rsync that are documented in the manual pages.</p>
<p>What should we put in options? Well, that is up to you. One very common option is the <code>--archive</code>, which combines several of the most commonly used flags. It copies over all the files in the source directory that are either not present in the destination, or have different content in the destination. Any files that are identical are not transferred.</p>
<h2 id="conclusion-and-future-considerations" tabindex="-1">Conclusion and Future Considerations</h2>
<p>Well there we have it! We have made a basic backup setup with rsync and discussed its benefits and weaknesses. This setup, although basic, is still very powerful and capable, but there is still a lot that we can improve:</p>
<ul>
<li>Scheduling: This can be done with crontab, systemd (linux) and Tasker (for android)</li>
<li>Trigger by file watch: Instead of invoking rsync at time intervals, you can use a file watcher that invokes rsync when a file is changed. This is particularly useful for notes. This is doable on linux but I am not sure if it is possible on Android.</li>
<li>Viewing Remote Files: We can use programs to view our files on the server from a remote client. <a href="https://github.com/awesome-selfhosted/awesome-selfhosted#photo-and-video-galleries">here is a list of self-hostable image &amp; video gallery programs</a></li>
<li>Use unprivileged user (security enhancement): this can be done by using a locked down user on the server side, so that the client cant mistakenly do too much damage</li>
<li>Run RSYNC Daemon with unprivileged user: many run rsync daemon with root. Some (like me) run it with an unprivileged user. This means you loose the chroot feature, but a potential vulnerability in rsync would be less destructive.</li>
<li>Use secure tunnel with RSYNC Daemon: As we discussed, rsync daemon is unencrypted and uses weak authentication. This is usually less of a problem for local network transfers, but you can use secure tunnels to mitigate it. Whether it is VPN, SSH tunnel, or other options, rsync can do them all! I might write a post about this as information about it is a bit scarce on the internet.</li>
</ul>
<p>Thank you for reading! This is one of my first posts, so please give me feedback if you have it.</p>

    ]]>
      </content>
    </entry>
  
    
    <entry>
      <title>Customizing Find and Replace in the Command Line</title>
      <link href="https://cosmicbyt.es/posts/find-and-replace-customize-cli/"/>
      <updated>2023-03-19T00:00:00Z</updated>
      <id>https://cosmicbyt.es/posts/find-and-replace-customize-cli/</id>
      <content type="html">
        <![CDATA[
      <h2 id="introduction" tabindex="-1">Introduction</h2>
<p>In this post, we will build commands for performing find and replace operations via the command line. We will first build it for a basic case, explore the customizations available to us, and build on top of that for more complex cases, such as performing the operation on a directory, previewing changes before committing them, adding a confirmation prompt, etc.</p>
<p>The goal of this article is not to just teach you how to find and replace in the command line. It is also aimed at teaching general command line skills and concepts, exploring the powers of the command line and the wealth of customizations it grants us.</p>
<p><strong>NOTE:</strong> I recommend you follow along. Open your terminal, create a test folder / directory and create random test files to apply the learnings you read here. Learning is best done by doing!</p>
<h2 id="why-not-use-text-editor-%2F-ide%3F" tabindex="-1">Why Not Use Text Editor / IDE?</h2>
<p>Just about all text editors and IDEs have a find and replace feature. Those are great to use if you are already in the middle of editing a file, but in my opinion, using the command line is better and faster (once you get used to it) for cases such as:<br>
</br></p>
<ul>
<li>You are not already in a text editor, and you only want to perform find and replace.</li>
<li>You want to apply the find and replace operation to multiple files. This is especially useful in a large codebase.</li>
</ul>
<p>You may disagree and find that the IDE is preferable. There's only one way to find out, so buckle up and get ready to see it in action!</p>
<h2 id="using-sed-to-find-and-replace" tabindex="-1">Using SED to Find and Replace</h2>
<p>sed is the ultimate utility for find and replace (though not the only one). sed, standing for <code>Stream EDitor</code>, takes a text input (which could also be a file) and performs filtering and transformations on it as specified by the user. This output can be optionally directed to a file, including the same file it was read from, or piped into another program (or standard output).</p>
<p>sed has many options for text manipulation commands, but the most common is the <code>s</code> command, standing for substitute. It is so popular that it is the only sed command many people know, and some even think it is sed's only functionality. sed has many other commands, and you can find them by reading the info page (run <code>info sed</code> in the command line, or visit the gnu website).</p>
<h3 id="basic-usage" tabindex="-1">Basic Usage</h3>
<p>To use sed for performing find and replace on an input, you can run the following:</p>
<pre><code>sed [options] 's/[regex-pattern]/[text-to-replace-with]/[flags]' [input-file]
</code></pre>
<p>the part in single quotes is called the &quot;command&quot; or &quot;script&quot; for sed. It starts with <code>s</code>, denoting substitute. The <code>regex-pattern</code> is a regex pattern for the text to replace. <code>text-to-replace-with</code> is, self-descriptively, what sed will replace the text matched by the pattern with.</p>
<p>Basic example:</p>
<pre><code>sed 's/patternToReplace/replaceWith/' somefile.txt
</code></pre>
<p>OR:</p>
<pre><code>echo 'random text with patternToReplace here' | sed 's/patternToReplace/replaceWith/'
</code></pre>
<h3 id="sed's-flags" tabindex="-1">SED's Flags</h3>
<p>Flags modify <code>sed</code>'s behavior, such as denoting how many of the matched patterns to replace, whether or not to be case insensitive, printing the line with a match, and some more advanced features, such as executing the result of the substitution as a shell command.</p>
<h3 id="replace-first-n-occurrences" tabindex="-1">Replace first N Occurrences</h3>
<p>To replace only the first N occurrences, we can use a number as a flag, denoting how many times to perform the find and replace operation.</p>
<pre><code>sed 's/replaceThis/replaceWith/2' somefile.txt
</code></pre>
<p>The above command will take <em>only</em> the first two occurrences of &quot;replaceThis&quot; and replace it with the value &quot;replaceWith&quot;. Without the flag, it would only replace the first occurrence.</p>
<h3 id="replace-all-occurrences" tabindex="-1">Replace All Occurrences</h3>
<p>we use the <code>g</code> flag, denoting global.</p>
<pre><code>sed 's/replaceThis/replaceWith/g' somefile.txt
</code></pre>
<h3 id="find-and-replace-in-place-(overwrite-file)" tabindex="-1">Find and Replace In-Place (Overwrite File)</h3>
<p>By default, when <code>sed</code> performs a find and replace (or any operation) on a file or text input, it prints the result to standard output. It does NOT save the modifications to the file. To save it in the same file we used as input, we can use the <code>-i</code> or <code>--in-place</code> flag:</p>
<pre><code>sed -i 's/replacethis/replacewith/' somefile.txt
</code></pre>
<blockquote>
<p><strong>NOTE For MacOS and BSD Users:</strong> The BSD version of <code>sed</code> (also used on MacOS) is slightly different than the GNU version of <code>sed</code> found on most linux systems. The <code>-i</code> flag is one of those differences, where the flag requires a value (at least an empty one). I recommend you check the documentation, but the command will look something like:</p>
</blockquote>
<blockquote>
<pre><code>sed -i '' -e 's/replacethis/replacewith/' somefile.txt
</code></pre>
</blockquote>
<blockquote>
<p>Alternatively, you can always install GNU <code>sed</code> on MacOS and BSD (available on homebrew and other package managers).</p>
</blockquote>
<h3 id="save-the-substitution-result-to-new-file" tabindex="-1">Save The Substitution Result to New File</h3>
<p>If, instead of overwriting to the same file, we want to save it to a new file, we just redirect the command's result:</p>
<pre><code>sed 's/replacethis/replacewith/' somefile.txt &gt; newfile.txt
</code></pre>
<p>the <code>&gt; newfile.txt</code> syntax is the redirect operator, which redirects the standard output of a command to a file (overwriting it if it already exists).</p>
<h3 id="there-are-many-more-options!" tabindex="-1">There Are Many More Options!</h3>
<p>This is only a subset of all features available in sed, and not even all the options for the substitute command! My intention is not to duplicate sed's documentation, but rather introduce you to <code>sed</code> and cover relevant concepts.</p>
<h3 id="alternatives-to-sed" tabindex="-1">Alternatives to SED</h3>
<p>sed is a great tool for this task, but are there alternatives? Of all popular utilities, <code>sed</code> is the simplest. More complex alternatives may grant you more features depending on what you are looking for, but it comes at a cost. You can even do what sed does with a full featured scripting languages that has a wealth of libraries, like python or nodejs. But for simple tasks, it is overkill and requires a lot more code.</p>
<p>However, there are tools and languages that only require a bit more code than sed, but optionally have a lot more features. For example, awk is one example as we will discuss below, and there is also perl / raku as a full featured scripting language that has a lot of text editing capabilities, and you can perform substitution with little code.</p>
<h4 id="awk" tabindex="-1">AWK</h4>
<p>A very common alternative to sed is awk. like sed, awk is also a text manipulation tool. There are a few differences:</p>
<ul>
<li>awk is able to recognize and manipulate delimited text such as tabulated data, columned text, csv, etc. sed has no concept or recognition of this beyond its regex capabilities.</li>
<li>awk is a much more complete programming language than sed. It has loops, conditionals, data structures like arrays, and the like.</li>
<li>Consequently, awk can perform analytics tasks such as calculating sums or counting data, or perform transformations onto the columns, such as removing certain columns or combining them.</li>
</ul>
<h2 id="recursive-find-and-replace-(in-a-directory-and-all-sub-directories)" tabindex="-1">Recursive Find and Replace (In a Directory and All Sub-directories)</h2>
<p>Now this is all great, but this is only slightly more impressive than text editor find-and-replace. Let uss see how the command line can extend the powers we already uncovered to apply the same functionality to multiple files.</p>
<p>The beauty of the shell (and I suppose most programming languages) is that we have constructs that allow us to apply a command to a dynamic input or operand (input here is the file(s) we are applying find and replace to). This dynamic input can be our list of many files. The question is, which files do we want to perform find and replace on? There are a few situations I have in mind:</p>
<ul>
<li>Apply to all files in current directory</li>
<li>Apply to all files in current directory and sub-directories (recursion)</li>
<li>Apply only to files in a directory that have a certain metadata (file extension, name, when it was last edited, etc)</li>
<li>Apply only to files in a directory based on the file's content (i.e. the file's content has a pattern match)</li>
</ul>
<p>There are many command line utilities that can help us here. I will focus on two: <code>find</code> and <code>grep</code>.</p>
<h2 id="find%3A-search-and-filter-files-by-metadata" tabindex="-1">find: Search and Filter Files by Metadata</h2>
<p>The find utility searches through all files and directories in a given directory based on their metadata. By default, it prints the file paths to standard output.</p>
<p>It has many useful options and filters. Things like: file or directory name, creation timestamp, modification timestamp, path, file size, etc. I will not go through them all (check the documentation or other resources).</p>
<h3 id="basic-usage-1" tabindex="-1">Basic Usage</h3>
<pre><code>find path/to/directory -type f -name &quot;*.md&quot;
</code></pre>
<ul>
<li>The <code>-type f</code> flag says: only return files, not directories.</li>
<li>The  <code>-name &quot;*.md&quot;</code> flag says: only return files whose name ends in <code>.md</code>, meaning a markdown file. The <code>*</code> is a wild card match character.</li>
<li>The command above will therefore return files inside <code>path/to/directory</code> that meet the two criteria above.</li>
</ul>
<p>We can even apply this to multiple directories:</p>
<pre><code>find path/to/directory path/to/second/directory -type f -name &quot;*.md&quot;
</code></pre>
<h2 id="executing-a-command-based-on-find-'s-output" tabindex="-1">Executing a Command Based on find 's Output</h2>
<p>Executing a command based on another command's output is very common in the command line world. This is very often done via <code>piping</code>; directing a command's standard output to the standard input of another command. We did this earlier with <code>sed</code> and <code>echo</code>:</p>
<pre><code>echo 'random text with PatternToReplace here' | sed 's/PatternToReplace/ReplaceWithThis/'
</code></pre>
<p>The <code>|</code> is the pipe operator.</p>
<p>This will not work with find. We will likely have more than one file being outputted by find, so we cannot simply pipe the result into sed, as it will think the file path/name is text and not a filename whose contents we want to modify. There are a few alternatives:</p>
<h3 id="command-substitution" tabindex="-1">Command Substitution</h3>
<p>The <code>$()</code> is the shell command substitution syntax. We can use:</p>
<pre><code>sed 's/somePattern/ReplaceWith/g' $(find some/path -type f)
</code></pre>
<p>What command substitution does is it executes the command inside the parentheses, and substitutes the result in its place instead. So the shell will substitute <code>$(find some/path -type f)</code> with the file paths, and only then will it execute the sed command.</p>
<p>One advantage of this method is that we are only invoking the sed command once, but with the input being multiple files. Now I haven't benchmarked this, but sometimes, invoking the command once with several files as input vs. a single invokation per file might perform better. However in some cases, that option is not even available, and a command can only accept one file as argument. Some of the below options might help.</p>
<h3 id="piping-with-xargs" tabindex="-1">Piping with xargs</h3>
<p>We said earlier that we cannot simply pipe the output of find into sed. However, we can pipe the output into a utility called <code>xargs</code>. <code>xargs</code> can take the output of a command that has multiple outputs like <code>find</code>, and then apply another command (in our case <code>sed</code>) once to every one of those outputs from the first command. Here is how we can do it:</p>
<pre><code>find path/to/directory -type f -print0 | xargs -0 sed -i 's/somePattern/replaceWith/g'
</code></pre>
<p>xargs will take the output of <code>find</code> command before the pipe operator, and execute the <code>sed</code> command for every one of <code>find</code>'s outputs.</p>
<p>Notice the <code>-print0</code> and <code>-0</code> flags. <code>-print0</code> tells find to separate its output using a null character, and <code>-0</code> in <code>xargs</code> tells it to look for the same said null character. This way, we make sure that whatever <code>find</code> uses to separate different paths in its output, <code>xargs</code> will use it to differentiate one file's path from another.</p>
<p>Like other command line utilities, xargs is very customizable and can fit many use cases, and there are many options documented in its documentation.</p>
<h3 id="using-find's-built-in--exec-option" tabindex="-1">Using find's Built-in -exec Option</h3>
<p>The options above are great and usable on various command line utilities. But <code>find</code> has its own built-in functionality for executing a command for every path matching its filters. This option is the <code>-exec</code> option.</p>
<pre><code>find path/to/directory -type f -name &quot;*.md&quot; -exec sed -i 's/somePattern/replaceWith/g' {} \;
</code></pre>
<p>We simply put the command after <code>-exec</code>.</p>
<p>However, notice two unusual pieces of syntax. First, the <code>{}</code>. This syntax tells <code>find</code> where to insert a path it matched as an argument for the <code>sed</code> command. When executing the command, <code>find</code> will replace <code>{}</code> with the path.  the <code>\;</code> is really just an escaped semicolon. The semicolon <code>;</code> is needed to tell <code>find</code> where the <code>-exec</code> command is done. This can be useful if you want to tack on other options to the find command.</p>
<p>One thing to note is that you can have multiple <code>-exec</code> in the same find command! There's a lot of potential customizations that can be made for more complex situations.</p>
<h2 id="adding-a-confirmation-prompt" tabindex="-1">Adding a Confirmation Prompt</h2>
<p>Suppose we want to find and replace, and but we are not sure we want to replace every instance of the pattern we match. One way of solving this is previewing the change and asking the user to confirm if they want to do it.</p>
<p>There are several ways of doing this. We can either script the confirmation and preview on our own, or we can use another of <code>find</code>'s built-in functionalities.</p>
<h3 id="find's--ok-option" tabindex="-1">find's -ok Option</h3>
<p><code>find</code>'s <code>-ok</code> option behaves very similarly to <code>-exec</code>, with one difference: before executing the command given, the user will be shown the exact command (after substitution) and ask the user to confirm if they want to execute it. Only then will the command be executed.</p>
<pre><code>find path/to/directory -type f -ok sed -i 's/somePattern/replaceWith/g' {} \;
</code></pre>
<p>But this only asks for confirmation. Before confirming, I would ideally want to look at the file and its contents. How can we do this?</p>
<h2 id="previewing-changes" tabindex="-1">Previewing Changes</h2>
<p>As we said earlier, <code>find</code> supports chaining multiple <code>-exec</code>s in order. This applies to <code>-ok</code> as well. So, we can add a command that would preview the change without modifying the file, then use <code>-ok</code> to ask the user if they would like to proceed.</p>
<h3 id="using-grep-to-show-state-before-modification" tabindex="-1">Using grep To Show State Before Modification</h3>
<p><code>grep</code> is an amazing and commonly used tool. It can search a file for a pattern and print to standard output the line(s) containing the pattern. We can use grep to show the state of the lines to be modified right before we modify it. It will also highlight the part of the line that will get modified. Since <code>grep</code> also uses regex, we can use the same regex we use with <code>sed</code>.</p>
<pre><code>find path/to/directory -type f -exec grep somePattern {} \; -ok sed -i 's/somePattern/replaceWith/g' {} \;
</code></pre>
<p>The above command is very similar to the one previous, except we added the <code>-exec</code> flag with the <code>grep</code> command. <span style="font-weight: 900; font-style: italic">The order here is very important</span>. The <code>grep</code> command will execute first, * then,* the <code>-ok</code> command will be shown to the user and executed upon confirmation, as discussed earlier.</p>
<p>The <code>grep</code> has a lot of customizations that can be explored. One common example is the <code>--context</code> flag, which shows you lines before and after the match. For example, <code>grep --context 2 --regexp somePattern [file-name]</code> will show two lines before and after the match. If you want only lines before the match or only after the match, there is <code>--after-context</code> and <code>--before-context</code>.</p>
<h2 id="conclusion" tabindex="-1">Conclusion</h2>
<p>This concludes the first chapter of our endeavor to explore options for find-and-replace in the command line, and explore the many options to customize them, and build up a command-line script to perform find-and-replace recursively with a confirmation prompt and preview (to avoid un-intended destructive behavior). The commands we built together are powerful on their own, but I hope that this post also helped you explore the endless possibilities for customization so you can fine-tune them to your needs, or use them on other commands, even unrelated to find-and-replace!</p>

    ]]>
      </content>
    </entry>
  
    
    <entry>
      <title>Unix Permissions - Part 1: Anatomy of Permissions</title>
      <link href="https://cosmicbyt.es/posts/unix-permissions-part-1-anatomy/"/>
      <updated>2023-04-20T00:00:00Z</updated>
      <id>https://cosmicbyt.es/posts/unix-permissions-part-1-anatomy/</id>
      <content type="html">
        <![CDATA[
      <h2 id="introduction" tabindex="-1">Introduction</h2>
<p><code>permission denied</code>: an error message that every command line user has faced before. Often times, users solve this by running <code>sudo [previous command]</code> (sometimes without considering consequences!). The error arises due to <strong>Traditional Unix Permissions System</strong>: The security mechanism that POSIX operating systems use by default, to protect the system from malicious actors, compromised processes, or even user mistakes and errors. Whether you aim to secure your system, or improve your productivity when working around an existing permission on your Linux or MacOS computer, learning how to use Unix permissions is a crucial part of system administration and day-to-day command line usage.</p>
<h2 id="summary" tabindex="-1">Summary</h2>
<p>In this article, we will talk about the traditional Unix permission system. We will talk about what it is exactly and how it works under the hood. We will break down the system into its individual peaces, and talk about each one individually, and how they all tie in together, to gain a full understanding of how it all works.</p>
<p>This is the first part of a series; the next parts will explore part of the suite of command-line tools offered to us to use and work around this security system, and look at an example of using the Unix permissions to architect our system's security model. Please check them out as well!</p>
<h2 id="pre-requisites" tabindex="-1">Pre-requisites</h2>
<p>Basic familiarity with a Unix-like system (Linux, macOS, or BSD) and the command line is encouraged, but not absolutely necessary.</p>
<h2 id="anatomy-and-model-of-unix-permissions" tabindex="-1">Anatomy and Model of Unix Permissions</h2>
<p>There are 5 main entities in traditional Unix Permissions:</p>
<ul>
<li><strong>User:</strong> The elementary unit. Has permissions associated with it</li>
<li><strong>Group:</strong> a group of users</li>
<li><strong>File:</strong> the entity we are imposing protections on</li>
<li><strong>Process:</strong> Gets assigned a user to gain its permissions</li>
<li><strong>File Permissions:</strong> denotes which users (and groups) have which accesses to a certain file</li>
</ul>
<p>To understand how each of those work together, let us explore them in detail one-by-one.</p>
<h3 id="user" tabindex="-1">User</h3>
<p>When you first hear the word &quot;user&quot;, you might imagine an actual person that uses the computer. This is how most people use them in Microsoft Windows, and indeed, Unix systems used them the same way initially. Most early computers were used by multiple people, and Traditional Unix permissions system was deliberately developed to secure users from each other. Our modern threats of malware or spyware from the internet were non-existent in that time.</p>
<p>However, times have changed. Nowadays, most computers are not multi-user, and in cases which they are, other users using the same machine are the least of your security worries. Threats coming from the internet (malware, hackers, etc) or vulnerabilities from the many applications installed on your system are far more important.</p>
<p>But the user model can still satisfy modern needs if we think of users differently. Instead of associating a user with a person, we conceptualize it as a unit that has a collection of permissions, and can be assigned to a process (including a login shell session) so that said process adopts that user's permissions. Upon attempting to access a file, the operating system will check the permissions associated with the file, and confirm that the user has the necessary permissions to perform the operation.</p>
<h3 id="group" tabindex="-1">Group</h3>
<p>A group is, as you'd expect, a group of users. It is a many-to-many relationship; a user can belong to multiple groups, and a group can have multiple users.</p>
<p>Like users, groups also have permissions associated with them. If a user is part of a group, they are granted the group's permissions.</p>
<p>As we will see later, groups are a key component in allowing many-to-many relationships between users and their permissions to access files. It is only with groups that we are able to grant accesses to a file to more than one user.</p>
<h3 id="permissions" tabindex="-1">Permissions</h3>
<p>Permissions are the core component of Unix permissions. Each file has three types of permissions:</p>
<ul>
<li><strong>Read:</strong> Ability to open the file and read its content (or copy it)</li>
<li><strong>Write:</strong> Ability to modify the file's content, completely overwrite it, or delete it</li>
<li><strong>Execute:</strong> Ability to run as a process (relevant for programs or scripts)</li>
</ul>
<p>These permissions are defined separately for each &quot;scope&quot;. A scope is a certain grouping of users. Each file has 3 scopes associated with it:</p>
<ul>
<li>The <strong>user</strong> that owns the file, or the owner.</li>
<li>The file's <strong>group</strong>, or group owner.</li>
<li><strong>other</strong>: All other users not included in the other scopes</li>
</ul>
<p>For example, for a specific file, we can say:</p>
<ul>
<li>The (user) owner has read and write access to the file.</li>
<li>The users belonging to the file's group have read access to the file. They can check out its content but cannot modify anything.</li>
<li>All other users have no permissions to this file. They cannot see its content or modify it</li>
</ul>
<p>This would be an example of a list of permissions associated with a file.</p>
<h3 id="files" tabindex="-1">Files</h3>
<p>Files are the element that Unix permissions system aims to protect. As we saw earlier, each file has a set of permissions associated, defining which users can do what. This is dictated by the file's owner and group, which are also defined separately for each file.</p>
<p>Upon creating a new file, it is usually assigned the owner and group based on the process or shell session that created it, but they can be changed. Setting the right owner and group is an important step in devising a security model to protect a certain file or your system as a whole. We will discuss this more deeply in later sections.</p>
<h2 id="how-it-really-works" tabindex="-1">How it Really Works</h2>
<p>Let us now describe how the operating system manages access in traditional Unix permissions. We will take a scenario where a certain user attempts to access a certain file. How exactly can we predict if the access will be granted?</p>
<h4 id="initial-setup" tabindex="-1">Initial Setup</h4>
<ul>
<li>We start with a regular Linux system</li>
<li>The system already has a set of files on its filesystem, and a set of users and groups defined</li>
<li>Each file on the syetm has a user owner and a group owner</li>
<li>Each file will have defined a list of permissions, denoting:
<ul>
<li>What accesses (read, write, or execute) the user owner has</li>
<li>What accesses the group owner has</li>
<li>What accesses all other users have</li>
</ul>
</li>
</ul>
<h4 id="putting-unix-permissions-to-test!" tabindex="-1">Putting Unix Permissions to Test!</h4>
<p>Let's see what it would look like if Unix permissions on the system we setup above are put to test.</p>
<ul>
<li>Suppose a user (or process) tries to access a file, the following happens</li>
<li>The system determies type of access requested. Is it read, write, or execute?</li>
<li>Next, the system determines which scope the user falls into
<ul>
<li>Is the user the user owner of the file? Then the scope is &quot;user&quot;</li>
<li>Does the user belong to the group owner of the file? THen the scope is &quot;group&quot;</li>
<li>If none of the above, the scope is &quot;other&quot;</li>
</ul>
</li>
<li>The system fetches the permissions associated with the scope determined in the previous step</li>
<li>The system checks whether the requested access in the list of permissions for that scope</li>
<li>If the system verifies that the requested access is in the permissions, then access is granted. Otherwise, access is rejected.</li>
</ul>
<blockquote>
<p>NOTE: This is a conceptual model to help us understand how it works, and is not meant to represent the exact implementation behind the scenes.</p>
</blockquote>
<h2 id="conclusion" tabindex="-1">Conclusion</h2>
<p>We now have a conceptual model of how Unix permissions work, which will be very handy when troubleshooting issues or securing a certain system or file. Whenever you come across a complex issue, trying to imagine the possible steps the system took to arrive at the error message it gave you can be the key to finding the solution, or they key to finding the vulnerability in your security model.</p>
<p><a href="/posts/unix-permissions-part-2-cli-apps/">In part 2 of this series,</a> we will dive into command line tools that help us work with permissions, such as setting or modifying permissions, or setting or modifying file owners or groups. Please check it out and give me feedback if you have it!</p>

    ]]>
      </content>
    </entry>
  
    
    <entry>
      <title>Unix Permissions - Part 2: chmod and Other Permission Command-line Apps</title>
      <link href="https://cosmicbyt.es/posts/unix-permissions-part-2-cli-apps/"/>
      <updated>2023-05-14T00:00:00Z</updated>
      <id>https://cosmicbyt.es/posts/unix-permissions-part-2-cli-apps/</id>
      <content type="html">
        <![CDATA[
      <h2 id="introduction" tabindex="-1">Introduction</h2>
<p>In <a href="/posts/unix-permissions-part-1-anatomy/">the previous part of this series,</a> we learned about Unix permissions and how they work, and established a conceptual model for understanding them. In this section, we will explore the practical side: How do we actually use Unix Permissions? How can I set or change permissions following the model from earlier?</p>
<p>We will look at various command-line applications that work with Unix permissions, and we will take a deep dive into two of the most important ones: <code>chmod</code> and <code>chown</code>.</p>
<h2 id="chmod" tabindex="-1">chmod</h2>
<p><code>chmod</code> is a utility to define or modify permissions of a file.</p>
<p>Recall above when we talked about permissions, and how every file has permissions for 3 access types (read, write and execute) for each of 3 scopes (user, group and others).</p>
<p><code>chmod</code> is precisely the utility that helps us set and define those permissions! It is the most important tool that we will discuss.</p>
<p>The command uses the following syntax:</p>
<pre><code>chmod [OPTIONS] [MODE],[ANOTHER MODE]... [FILENAME]
</code></pre>
<p>The &quot;MODE&quot; is the important part here. It is basically a way to express the &quot;permissions&quot; that we want to define or change.</p>
<p>There are two different syntaxes for mode: symbolic mode, and octal mode. Regular mode is verbose, whereas octal mode uses numbers to make a shorter command. Let us explore them both.</p>
<h3 id="symbolic-mode-syntax" tabindex="-1">Symbolic MODE Syntax</h3>
<p>We can summarize symbolic mode syntax as follows: <code>[scope][operator][accesses]</code>.</p>
<p><code>[scope]</code> is the same scope we discussed when we spoke about permissions earlier. The scope is either the owner, group, or others.</p>
<p>There is one more category that <code>chmod</code> allows: &quot;all&quot;, which denotes all of the scopes. Using all would be the equivalent of defining the same permissions separately for each scope. You can do it all in one MODE instead.</p>
<p>Each scope is denoted by a single letter in the symbolic MODE syntax:</p>
<ul>
<li><code>u</code> for the user owner</li>
<li><code>g</code> for the group owner</li>
<li><code>o</code> for all other users</li>
<li><code>a</code> for all the above</li>
</ul>
<p><code>[accesses]</code> is the access types we spoke of previously, and they are also each denoted by single letters:</p>
<ul>
<li>read, denoted by <code>r</code></li>
<li>write, denoted by <code>w</code></li>
<li>execute, denoted by <code>x</code></li>
</ul>
<p>Finally, the <code>operator</code> tells us what exactly to do with the access types for the specific scope:</p>
<ul>
<li><code>+</code> to add access to existing accesses</li>
<li><code>-</code> to remove from existing accesses</li>
<li><code>=</code> to set the accesses. In other words, those not listed in our mode will be removed</li>
</ul>
<p>Each mode has exactly one scope and one operator, but can have one or more of the accesses. In other words, a mode can change only one access type for a given scope, or it can change all 3 for that scope.</p>
<p>To change multiple scopes, you need to write a separate symbolic mode for each.</p>
<p>For example, if we want to add read and write access for the group owner, we can do the following:</p>
<pre><code>chmod g+rw someFile.txt
</code></pre>
<p>We used the plus <code>+</code> operator here, which as we said, <em>adds</em> accesses and does not set them. This means that if the file had execute access for the group owner, it would still be there. If we want to make sure only the accesses we declare in the mode persist, we must use the equal <code>=</code> operator:</p>
<pre><code>chmod g=rw someFile.txt
</code></pre>
<p>Suppose that, simultaneously, we want to remove write access from &quot;all other users&quot;. To do this, we can add a another mode. Modes are comma separated.</p>
<pre><code>chmod g=rw,o-rw someFile.txt
</code></pre>
<p>It is important to note that there are other options for MODE that I did not discuss here. Please feel free to read the documentation for <code>chmod</code> to learn more interesting things you can do with permissions.</p>
<h3 id="octal-mode-syntax" tabindex="-1">Octal MODE Syntax</h3>
<p>Octal MODE syntax is the most commonly used syntax. It simplifies MODE syntax to use only 3 digits. Each digit represents which permissions are to be defined for each scope. We provide 3 digits to define the permissions for the 3 different scopes.</p>
<p>The 3 digits define permissions for the 3 scopes in the following order:</p>
<ol>
<li>User</li>
<li>Group</li>
<li>Others</li>
</ol>
<p>or:</p>
<pre><code>chmod [user digit][group digit][all digit] someFile.txt
</code></pre>
<p>With each digit denoting the access types we want to set for the given scope.</p>
<p>How do we determine what number goes with which level access? well it goes like this:</p>
<ul>
<li>1 for <code>x</code> or execute access</li>
<li>2 for <code>w</code> or write access</li>
<li>4 for <code>r</code> or read access</li>
<li>0 for no access</li>
</ul>
<p>So, if I want write access for the user, read access for the group, but no access for the rest, we can do:</p>
<pre><code>chmod 240 someFile.txt
</code></pre>
<p>Keep in mind, the permissions here are being <em>set</em>, not added to existing permissions. In other words, if &quot;other users&quot; had any access prior, they go away. If group user had write access or execute access prior, it also goes away.</p>
<p>But what if we want more than one access? what if we want both read <em>and</em> write access for the user? Don't sweat it, we are in luck! The numbers were chosen carefully so that they can represent assigning multiple permissions in a single digit. To do so, simply add the digits together for the permissions you want. The numbers were chosen such that every sum is unique, and <code>chmod</code> can deduce which accesses you intend to give with just a single digit.</p>
<p>If we want both read and write access? we just add the digits for read and write: 2+4=6.</p>
<p>So if we want read and write access for user, read access for group, and read access for all others:</p>
<pre><code>chmod 644 someFile.txt
</code></pre>
<p>What if we want read, write and execute for the user, read and write for the group, but only read for everyone else?</p>
<pre><code>chmod 764
</code></pre>
<p>Congratulations! you just learned how to use <code>chmod</code>!</p>
<blockquote>
<p><strong>NOTE:</strong> Only the file's owner and the root user is able to execute <code>chmod</code>.</p>
</blockquote>
<h2 id="chown" tabindex="-1">chown</h2>
<p>Standing for &quot;change owner&quot;, <code>chown</code> does exactly what you just head. It takes a file and changes the owner. This includes both the user owner and optionally the group.</p>
<p>This is very important. While <code>chmod</code> allows us to change the &quot;permissions&quot; that are applied to a file's owner and group, it is just as necessary that we are able to set these owners and groups. This defines the set of users for which we are setting or granting permissions.</p>
<p>To change the owner with <code>chown</code>, we can:</p>
<pre><code>chown nezar someFile.txt
</code></pre>
<p>which sets the owner of the file to the user &quot;nezar&quot;.</p>
<p>To change the group as well, we have to separate the user and group by a colon <code>:</code></p>
<pre><code>chown nezar:movies someFile.txt
</code></pre>
<p>This changes the owner to &quot;nezar&quot; and the group to &quot;movies&quot;.</p>
<blockquote>
<p><strong>NOTE:</strong> Only root user is able to change the owner of a file. However, the owner user <em>can</em> change the group, but only to other groups they belong to. Otherwise, root privileges are required.</p>
</blockquote>
<h2 id="other-tools" tabindex="-1">Other Tools</h2>
<p>There are a few other tools that are helpful. Below are examples of some of those tools. I will not cover them in depth, but feel free to explore them on your own.</p>
<ul>
<li><strong><code>ls -l</code></strong>: The <code>ls</code> command lists all files in a directory. The <code>-l</code> option displays more details on each file, including its permissions</li>
<li><strong>useradd:</strong> Creates a new user</li>
<li><strong>groupadd:</strong> Creates a new group</li>
<li><strong>usermod and groupmod:</strong> Modify existing users and groups. Helpful to assign a user to a group,or change the groups a user belongs to</li>
<li><strong>userdel and groupdel:</strong> Delete users and groups</li>
<li><strong>chgrp:</strong> very similar to <code>chown</code> but only for changing a group</li>
<li><strong>umask:</strong> Change the default permissions assigned to newly created files in a directory</li>
<li><strong>newgrp:</strong> Change the group assigned to the current login session</li>
<li><strong>su:</strong> Change the current user or run a command as another user</li>
<li><strong>sudo and doas:</strong> More advanced versions of the <code>su</code> command</li>
</ul>
<h2 id="conclusion" tabindex="-1">Conclusion</h2>
<p><a href="/posts/unix-permissions-part-3-devising-security-model">In the next part of this series,</a> we will look at how we can use what we learned so far to secure a file or a system using Unix Permissions. Please remember to provide feedback, it will help me write better in the future!</p>

    ]]>
      </content>
    </entry>
  
    
    <entry>
      <title>Unix Permissions - Part 3: Devising a Security Model</title>
      <link href="https://cosmicbyt.es/posts/unix-permissions-part-3-devising-security-model/"/>
      <updated>2023-06-07T00:00:00Z</updated>
      <id>https://cosmicbyt.es/posts/unix-permissions-part-3-devising-security-model/</id>
      <content type="html">
        <![CDATA[
      <h2 id="introduction" tabindex="-1">Introduction</h2>
<p>In the <a href="/posts/unix-permissions-part-2-cli-apps/">last sections of this series,</a> we explored various CLI applications that work with Unix permissions and learned about how the permission system really works. In this last part of the series, we will look at using those learnings to secure files and systems.</p>
<p>Our increased reliance on the internet and computers for sensitive data makes digital security increasingly important. We will look at how Unix permissions fall in a general threat scenario, and which threat models it can defend against, and which it is unideal to defend against.</p>
<h2 id="threat-models-unix-permissions-can-defends-against" tabindex="-1">Threat Models Unix Permissions Can Defends Against</h2>
<p>Unix permissions can defend against attacks that target files on the file system, as that is what they are designed to protect. It can protect these files from:</p>
<ul>
<li>A process or application that is malicious, buggy or compromised</li>
<li>A malicious or incompetent user who has shell access (physically or remotely via SSH)</li>
</ul>
<p>If a file is protected with the right permissions, the malicious actor could be prevented from reading or stealing the contents of a file, modifying or overwriting the content, or deleting the file entirely.</p>
<h2 id="threat-models-where-unix-permissions-will-not-help" tabindex="-1">Threat Models Where Unix Permissions Will Not Help</h2>
<ul>
<li><strong>An attacker who has root shell access:</strong> root access can bypasses Unix permissions</li>
<li><strong>A process that has root access</strong></li>
<li><strong>An attacker that has physical access to the hardware:</strong> With physical access, the attacker can separate the hardware from the operating system, bypassing the operating system's protections. They can connect the hardware to another computer to access the files, for example.</li>
<li><strong>Data not on the filesystem:</strong> Traditional permissions can only protect files. Data in network transit or displayed on a graphical application displayed on a monitor are not covered.</li>
</ul>
<p>As we can see, Unix permissions are necessary but not sufficient. There are other security layers that can be responsible for the thread models where Unix permissions are insufficient, but they are out of scope for this article.</p>
<h2 id="devising-a-security-model-using-traditional-unix-permissions" tabindex="-1">Devising a Security Model Using Traditional Unix Permissions</h2>
<p>Now that we have gained an understanding of Unix permissions and how to work with them, we can actually start using them! What we learned so far is sufficient for working with individual files or directories on a case-by-base basis, but what about if we take a step back and look at the big picture of securing our system?</p>
<p>We already have the necessary skills of interacting with the permission system, but the system is flexible enough that there are several ways of going about using traditional permissions to devise a security model for your system. This can range from giving everything permissive permissions to make matters as easy as possible without much regard to security, or we could go totally overboard while sacrificing convenience and ease of use.</p>
<p>I will discuss with you a security model that I recommend. It may be overkill for some, but it would still be valuable to learn, and you can feel free to adjust it yourself as you see fit, as it is easy to only partially apply this strategy.</p>
<h3 id="least-privilege-principle" tabindex="-1">Least Privilege Principle</h3>
<p>The least Privilege Principle tells us that every user must only given the minimum permissions necessary for them to perform the tasks they need. This means that any malicious or compromised actor is limited in the damage they could possibly cause and the data they can access. This is what is sometimes referred to as the <strong>attack surface.</strong></p>
<p>If a certain user is given more permissions that they need, there is little benefit to doing so. At the same time, it risks increasing the attack surface if this user is ever compromised. This means that reducing permissions to the minimum needed is beneficial.</p>
<p>Moreover, this means that having one or few users have all or most permissions is undesired. Instead, having more users, each having a smaller subset of permissions, can lead to increased security.</p>
<p>Imagine, for example, that a process got compromised, and the attacker can now manipulate this process. If we did not follow the principle of least privilege, the process could end up having the ability to cause a lot of damage or compromise a lot of data. The attacker can manipulate data, steal it, or control other processes. But if least privilege principle was followed, even with a compromised process, the attacker may end up with little to nothing of interest to do.</p>
<p>We model our system in a way such that an attacker ends up in this situation: even in the case that the attacker compromises a process somehow, the process is very limited and hence the damage they can do is limited as well. Even if we secure the process that we think it is unlikely to be compromised, we can never be sure, and there is little sacrifice in implementing least privilege.</p>
<p>So how exactly do we use traditional permissions to implement least privilege principle?</p>
<h3 id="assigning-users-to-access-patterns" tabindex="-1">Assigning Users to Access Patterns</h3>
<p>Let us start with our users. Recall that users are the elementary unit that has permissions assigned, and can request access based on those permissions. Therefore,this is the entity whose permissions we want to limit as much as possible. We associate users with certain access patterns, such as:</p>
<ul>
<li>a certain program or process we expect to run, such as a web-server</li>
<li>a script that will get auto-triggered</li>
<li>a remote device or script accessing our system via SSH</li>
<li>an actual user / person</li>
</ul>
<p>Each of those should have their own users that have only the least amount of permissions needed to perform what they need. Whenever a new user is created, you must carefully determine what accesses they need and only grant them that access.</p>
<h3 id="granting-permissions-via-groups" tabindex="-1">Granting Permissions Via Groups</h3>
<p>Now that we figured out how to distribute users, let's give them the permissions they need!</p>
<p>Recall that a user can gain permission to a file by being part of one of three scopes: the user owner of a file, part of the group owner of the file, or all other users.</p>
<p>If we want to grant a user access to a file, our natural inclination might be to just make that user the owner of the file, and define permissions as such. This model is insufficient in many cases, as there can only be one owner per file, and thus deprives us from the ability of granting access to more than one user.</p>
<p>It would also not be wise to rely on the &quot;all other users&quot; scope, as this deprives us of the granularity of choosing which users are granted access to a file. Yes, we do not want to grant access to only one user, but we also don't want to grant access to unvetted users.</p>
<p>This leaves us with the last scope: the group. Controlling access by modifying permissions of the file's group and adding users we want to grant permissions to said group gives us the best balance. We can control which users get access, and what access they get.</p>
<p>This is how we arrive to our approach: to give a user permissions to a file, they must belong to the file's group.</p>
<h3 id="modeling-and-organizing-groups" tabindex="-1">Modeling and Organizing Groups</h3>
<p>If we grant a user access to a file by assigning it to the file's group, we would then organize groups by associating a group with a single category of access, or more precisely a group of files that have common access expectations.</p>
<p>Let's take an example. Suppose we have a directory that includes a set of private notes. To follow the model we devised, we would create a group for all of those notes. We will conveniently call it &quot;notes&quot;:</p>
<pre><code># create the group
groupadd notes
# assign the notes directory to the notes group
chgrp notes notes/
</code></pre>
<p>So now, whenever we want to grant any user access to &quot;notes&quot;, we would just add it to that group. To do this with the user &quot;nezar&quot;:</p>
<pre><code>usermod --append --groups notes nezar
</code></pre>
<p>and now the user nezar is granted access to notes!</p>
<p>Of course, we must remember to set the permissions for users and groups correctly, and to choose a good user owner for the directory:</p>
<pre><code>chmod --recursive 660 notes/
chown root: --recursive notes
</code></pre>
<p>the above makes the user owner and the group able to read and write the files, and all other users unable to read, write or execute the files. Then, it sets root as the user owner.</p>
<p>And we can follow this model for all other files and directories. To summarize:</p>
<ul>
<li>identify a semantic grouping of files that would have common permissions for a set of users</li>
<li>create a group with a name semantically describing that group of files, similar to what we did with our &quot;notes&quot;</li>
<li>assign the files to the group with <code>chgrp</code> or <code>chown</code></li>
<li>define the permissions for the files with <code>chmod</code></li>
<li>add users to the group using <code>usermod --append --group ...</code> to grant them access</li>
</ul>
<h2 id="limitations-and-alternatives-of-unix-permissions" tabindex="-1">Limitations and Alternatives of Unix Permissions</h2>
<p>The traditional Unix permissions model is simple yet powerful. Its biggest benefit is that it is the default on most Unix-like systems, but sometimes it is too simple and limited.</p>
<p>For example, traditional permissions may allow us to define permissions for a group of users, but it does not allow us to set different permissions for different groups of users. We can only set different permissions for the 3 scopes granted to us: the user owner, the group, and all other users. One of those can only fit one single user, and the other grants access to a group of access without any granularity. This means we cannot define permissions for two or more specific groups of users.</p>
<p>Thankfully, there are alternatives. I will list some of them but not cover them in this post, but look out for future posts as I may write about them in the future!</p>
<ul>
<li>POSIX Access Control Lists</li>
<li>AppArmor</li>
<li>SELinux</li>
</ul>

    ]]>
      </content>
    </entry>
  
    
    <entry>
      <title>Demistifying Containers and Docker</title>
      <link href="https://cosmicbyt.es/posts/demistifying-containers-part-1/"/>
      <updated>2023-06-09T00:00:00Z</updated>
      <id>https://cosmicbyt.es/posts/demistifying-containers-part-1/</id>
      <content type="html">
        <![CDATA[
      <h2 id="introduction" tabindex="-1">Introduction</h2>
<p>Containers have swiftly taken over the software development world over the past decade, saving many software developers tons of time. But it is often taken for granted, as it &quot;just works&quot; and stays out of your way with its black box magic.</p>
<p>But when the time comes that you have to mess with it, unfamiliarity can bring a lot of friction, and you may wonder why your team is introducing this seemingly unnecessary complexity. &quot;Why can't we just run the app locally like normal people?!&quot; you may think. Well today, I am here to tell you that containers provide you a ton of value!</p>
<h2 id="summary" tabindex="-1">Summary</h2>
<p>In this series, we will break the mystery surrounding the black box magic of containers. In this post in particular, we will start with asking ourselves: what are containers, and what is a container actually doing when it is running our application inside of it? what difference does it make to run the application locally vs. running it in a container? What problems does containerization solve?</p>
<p>We will focus on standard containers as defined by the Open Container Initiative, of which Docker is part. There are other types of containers that are out of scope for this post.</p>
<h2 id="application-dependence-on-environment" tabindex="-1">Application Dependence on Environment</h2>
<p>When developing software, you are not only dealing with your application's code. There are piles of dependencies onto which your code is stacked and relies upon to behave as expected. I am not only talking about dependencies that your code explicitly lists as packaged dependencies in your favorite package manager; there are many other dependencies that are less visible. For example:</p>
<ul>
<li>programming language's runtime or compiler</li>
<li>Operating System and Distribution (Debian, Alpine, CentOS, Red Hat, etc.)</li>
<li>Web server or reverse proxy (nginx, apache, etc.)</li>
<li>Operating system libraries and applications (glibc, the shell, etc).</li>
<li>cryptographic dependencies (e.g. OpenSSL, GPG)</li>
<li>Database (PostgreSQL, MySQL, MongoDB, etc.)</li>
</ul>
<p>And there are likely many others, depending on the application. Each of those has its own explicit and implicit dependencies. Consider that a difference in the versions of one of those dependencies, or in their configuration or setup, may cause a variation or failure in your application's execution. Imagine how hard it is to track down the source of this change in behavior.</p>
<p>This is only exacerbated if the application is running on a computer that is not even your's. How could you possibly know the environment details of a remote computer where your application is running?</p>
<h2 id="reproducibility-of-environments" tabindex="-1">Reproducibility of Environments</h2>
<p>The natural solution to the issue of the reliance on environment for an application to work properly is to tell the user how to setup the correct environment, via a setup guide (README.md). This is harder for consumer-facing applications, as it relies on technical competency of the user. Often times, however, and especially for enterprise applications (which is the focus for this post), the &quot;user&quot; is another developer (or devOps engineer) using or deploying the application.</p>
<p>However, even when the user is technically competent, there is still large room for error, and it is almost impossible to nail down every possible error the user can do.</p>
<p>There is no telling what is installed on the user's computer. With millions of software applications out there, do you really know how each of them will affect your application?</p>
<p>And even if you do, the constantly changing state of your application, and updates to the environment, would require constant re-evaluation of this setup guide. Will you go through re-setting up your environment every time you make changes to your codebase?</p>
<p>Another computer may have a different operating system, a different set of applications they have pre-installed, or certain configurations. Will your setup guide cover all those edge cases or tell the user if their edge case is not covered?</p>
<p>If only there was a way to automatically setup an environment without the requirement of user intervention ... and somehow make this environment isolated, such that it is controlled, predictable, and does not effect the rest of the computer ... and somehow make all of this portable and very easy for someone to take it and run it on their own computer ...</p>
<p>That is precisely what Containerization technology aims to do!</p>
<h2 id="what-are-containers" tabindex="-1">What are Containers</h2>
<p>For ease of understanding, let's break down the individual pieces of the definition. A Container is a:<br>
</br></p>
<ul>
<li>isolated and restricted environment</li>
<li>encapsulates an application and <strong>all</strong> of its dependencies (down to OS libraries, <a href="#containers-share-a-single-host-kernel">but not the kernel</a> )</li>
<li>have predictable behavior regardless of underlying infrastructure</li>
<li>runs on a host Linux kernel shared with other containers</li>
</ul>
<p>The above are what we need for this first part of the series. For the next parts of this series, we will unpack that containers:</p>
<ul>
<li>have standardized tools and methods to operate (create, run, stop, snapshot, etc.)</li>
<li>can be uploaded and downloaded and ran by a runtime without modification</li>
</ul>
<p>Containers are specified in greater detail in the <a href="https://github.com/opencontainers/runtime-spec/blob/main/spec.md">Open Container Initiative Specification</a>, which is an effort to standardize containerization technology, of which Docker is a part.</p>
<p>Let's break down this definition further.</p>
<h2 id="isolation" tabindex="-1">Isolation</h2>
<p>Containers are isolated environments, and this is one of the most important concepts. Isolation helps keep the environment controlled and the application's execution predictable. What exactly does this mean? what exactly is isolated?</p>
<p>Container isolation occur at multiple levels: file system isolation, processes, user IDs, network, system resources (RAM, CPU, etc) and others.</p>
<p>To achieve this, containers use existing Linux features: Namespaces and Control groups (cgroups). cgroups allow isolation and restriction of system resources, while namespaces handle most of the rest.</p>
<h3 id="file-system-isolation" tabindex="-1">File System Isolation</h3>
<p>containers provide an isolated file system. In other words, the container has its own file system that is separate from the host computer's and other containers' file systems.</p>
<p>To better understand this, think of the file system of your current computer. On linux, if you look at the root path, you will find this:</p>
<pre class="language-bash"><code class="language-bash"><span class="token punctuation">[</span>user@computer /<span class="token punctuation">]</span>$ <span class="token function">ls</span> /<br>bin   dev  home  mnt         proc run   srv   sys  usr<br>boot  etc  lib   lost+found  opt  root  sbin  tmp  var</code></pre>
<p>now imagine if a similar structure to this is replicated, but under a subdirectory.</p>
<pre class="language-bash"><code class="language-bash"><span class="token punctuation">[</span>user@computer ~<span class="token punctuation">]</span>$ <span class="token function">mkdir</span> /home/user/sub-directory<br><span class="token punctuation">[</span>user@computer ~<span class="token punctuation">]</span>$ <span class="token builtin class-name">cd</span> /home/user/sub-directory<br><span class="token comment"># create all </span><br><span class="token punctuation">[</span>user@computer ~<span class="token punctuation">]</span>$ <span class="token function">mkdir</span> bin dev home mnt <span class="token punctuation">..</span>.</code></pre>
<p>So now we have a structure that is very similar to our root, except all the folders are empty. But it is starting to resemble its own small computer inside our big one!</p>
<p>To take this further, we can use <strong>change-root</strong>, or <code>chroot</code>. change-root changes the root directory. The root directory is the directory accessible by the <code>/</code> path, like when you do <code>cd /</code> or <code>ls /</code> like we did above. But chroot allows us to change this (temporarily) to another sub-directory to perform certain commands.</p>
<p>For chroot to work, we need at least one program to be present so we can run it. We will use <code>bash</code>, which can usually be found under <code>/bin/bash</code>.</p>
<pre class="language-bash"><code class="language-bash"><span class="token comment"># print current working directory</span><br><span class="token punctuation">[</span>user@computer ~<span class="token punctuation">]</span>$ <span class="token builtin class-name">pwd</span><br>/home/user/sub-directory<br><span class="token punctuation">[</span>user@computer ~<span class="token punctuation">]</span>$ <span class="token function">cp</span> <span class="token parameter variable">-a</span> /bin/bash ./bin/bash<br><span class="token comment"># copy libraries used by bash</span><br><span class="token comment"># For convenience, we will copy the entire lib directory</span><br><span class="token punctuation">[</span>user@computer ~<span class="token punctuation">]</span>$ <span class="token function">cp</span> <span class="token parameter variable">-a</span> /lib ./<br><span class="token punctuation">[</span>user@computer ~<span class="token punctuation">]</span>$ <span class="token function">cp</span> <span class="token parameter variable">-a</span> /lib64 ./<br><span class="token comment"># chroot syntax: chroot [path to chroot into] [command to run]</span><br><span class="token punctuation">[</span>user@computer ~<span class="token punctuation">]</span>$ <span class="token function">chroot</span> ./ /bin/bash<br>bash-5.1<span class="token comment">#</span></code></pre>
<blockquote>
<p>To exit the change-rooted environment, use &quot;exit&quot; command.</p>
</blockquote>
<p>Now we have entered the change-rooted environment! It is not very useful at the current state, though. If we try to run most things, chances are it will error out:</p>
<pre class="language-bash"><code class="language-bash">bash-5.1<span class="token comment"># ls</span><br>bash: ls: <span class="token builtin class-name">command</span> not found</code></pre>
<p>the <code>ls</code> command does not exist in our isolated environment. This means that whenever we load our own application in here, it will only use programs it can find in the isolated environment, which is exactly what we are looking for!</p>
<blockquote>
<p><strong>Note:</strong> While <code>chroot</code> is a useful analogy, it does not create a secure file system isolation, and it is possible for a malicious program to affect the host file system. Moreover, contrary to popular belief, standard containers don't use <code>chroot</code> anymore, but use <code>pivot_root</code> instead.</p>
</blockquote>
<blockquote>
<p><strong>Note 2:</strong> change-rooting is not the only mechanism used for file system isolation. Namespace mount isolation is also used. It is very similar to process isolation, which also utilizes namespaces, and is discussed in the next section.</p>
</blockquote>
<h3 id="process-isolation" tabindex="-1">Process Isolation</h3>
<p>A process in Linux is a running program. When a program is ran, it is assigned a process ID, which helps keep track of it, and perform operations like stopping or pausing it, launch spin-off processes, allocating resources, and so on.</p>
<p>to check currently running processes, we can use the <code>ps</code> command.</p>
<pre class="language-bash"><code class="language-bash"><span class="token punctuation">[</span>user@computer ~<span class="token punctuation">]</span>$ <span class="token function">ps</span> <span class="token parameter variable">-au</span><br>    PID TTY          TIME CMD<br>    <span class="token number">345</span> tty1     00:00:10 sway<br>    <span class="token number">398</span> tty1     00:00:02 swaybar<br>    <span class="token number">610</span> tty1     00:00:00 swaybg<br>   <span class="token number">1841</span> tty1     00:00:01 <span class="token function">vim</span><br>   <span class="token number">2155</span> tty1     00:00:05 <span class="token function">bash</span><br>   <span class="token number">3001</span> tty1     00:00:08 <span class="token function">node</span></code></pre>
<p>Containers use a Linux feature called namespaces, in which Linux can create a brand new <em>process namespace</em> for the container. You can imagine it as an isolated, blank space fo the containers processes and process IDs. Processes of other containers (and the host) cannot be &quot;seen&quot; by processes inside the container, or be influenced or manipulated by them. It looks as if only the container's processes are the ones that exist. You can verify this by running the <code>ps</code> command inside the container to see the visible processes inside.</p>
<p>Linux namespaces support more than just process namespaces! Let's talk about few of the important ones.</p>
<h3 id="network-namespace" tabindex="-1">Network Namespace</h3>
<p>Containers also use network namespaces for network isolation. This helps prevent network collisions and cross-container influences. For example, suppose you have two applications choosing to run on the same port 3000. Since network ports are namespaced, you can avoid these collisions. Both containers can run on port 3000 in their own network namespace, which are isolated from each other.</p>
<p>We can also map port 3000 from each of those containers to a different port on the host, giving them access to and from the wider network.</p>
<h3 id="user-namespace" tabindex="-1">User Namespace</h3>
<p>Containers can create their own users and groups that are separate from other containers and the host system. I spoke extensively about users and groups in my <a href="/posts/unix-permissions-part-1-anatomy/">series on traditional Unix permissions</a>.</p>
<h3 id="control-groups" tabindex="-1">Control Groups</h3>
<p>control groups, or cgroups, are another linux feature that allow isolating and restricting system resource utilization by containers. This allows us to limit resource usage by containers, or ensure that they are provided sufficient resources to run.</p>
<h2 id="containers-share-a-single-host-kernel" tabindex="-1">Containers Share a Single Host Kernel</h2>
<p>Although containers strive to create isolated environments, their isolation stops at the kernel level. Everything above the kernel, such as Operating System libraries and user land applications are isolated, but the kernel itself is not.</p>
<blockquote>
<p><strong>Wait a minute, what even is a kernel?</strong><br>
The kernel is the program that acts as the first layer that interacts with the hardware. It abstracts many of the varying hardware features and makes it available to user land programs via its API.<br>
Moreover, the kernel is also what makes it possible to run multiple applications at once and schedule them. It manages resource allocation between those different processes. The kernel is very important, and most of our code is written with the assumption that there is a Kernel handling those tasks.</p>
</blockquote>
<p>Instead, containers either use the same host kernel if it is Linux, or they emulate a Linux kernel and run all the containers on the same emulated kernel.</p>
<p>Virtual Machines (VMs) are another tool used for creating isolated environments. They emulate the hardware, and each VM has its own instance of a kernel running, providing deeper isolation.</p>
<h4 id="why-not-use-vms-then%3F" tabindex="-1">Why not Use VMs then?</h4>
<p>VMs are much slower, since emulating hardware and running a separate kernel is a resource-intensive task. It creates subpar performance, and for not that much added benefit. The Kernel is rarely ever the cause of software incompatibility, and it is usually the same across different machines.</p>
<p>Moreover, containerization is a technology for software development productivity. Its main purpose is easing the software development and deployment cycle, and thus provide tons of features making them very easy to use.</p>
<p>While VMs provide a ton of added security, that is usually not why containers are used. Containers are used to make development easier. To ensure that the execution of our application is predictable and reproducible. We don't like surprises in production!</p>
<h2 id="conclusion" tabindex="-1">Conclusion</h2>
<p>Containers avoid developers a ton of wasted time. They allow us to keep the execution of our applications predictable, and to avoid the issues caused by differing system environments across different computers.</p>
<p>We saw how containers create isolated environments using Linux features. They isolate the file system, the process space, and others, in order to rule out the possibility of interference from the host environment.</p>
<p>This demistifies what a running container looks like, and what exactly the runtime does to achieve this. In the next part of this series, we will look at how containers are created and built, and how it is ensured that this build process yields the output we expect. Stay tuned!</p>

    ]]>
      </content>
    </entry>
  
    
    <entry>
      <title>What is the Fediverse, and Frequently Asked Questions</title>
      <link href="https://cosmicbyt.es/posts/what-is-fediverse-faq/"/>
      <updated>2023-06-09T00:00:00Z</updated>
      <id>https://cosmicbyt.es/posts/what-is-fediverse-faq/</id>
      <content type="html">
        <![CDATA[
      <h2 id="introduction" tabindex="-1">Introduction</h2>
<p>Social media is a constantly evolving space, with new ideas coming out all the time and new platforms overtake others, or establish new unexplored territories. I would even argue that social media has become the core part of the internet. It has overtaken the early internet dominated by individual websites and forums.</p>
<p>The Fediverse is one of those ideas in social media that has a very interesting perspective, both from a social and a technical point of view. While the Fediverse has not overtaken mainstream social media by any means, it has occupied a firm niche with millions of active users (accoring to <a href="https://the-federation.info">The-Federation.info</a>), and the number seems to increase in sudden bursts in response to certain events, like Elon Musk's buying of Twitter and Reddit charging for API usage by third party clients.</p>
<p>The biggest problem with the Fediverse is that it is intimidating and mysterious to new comers, and seasoned users frequently repeat terms sounding like jargon to those unfamiliar. Terms like <code>instance</code>, <code>federates</code>, seem to confuse people, and there's a degree of choice paralysis among new users regarding which &quot;instance&quot; to chose when creating an account.</p>
<h2 id="summary" tabindex="-1">Summary</h2>
<p>In this article, I hope to demystify some of the Fediverse concept as a new curious observer myself with limited experience. We will talk about what the fediverse is, how it works on a high level, some frequently asked questions, and how to get started if you are interested. I will not be discussing the ActivityPub protocol or any implementations, as this currently falls outside of my scope of knowledge.</p>
<h2 id="what-is-the-fediverse%3F" tabindex="-1">What is the Fediverse?</h2>
<p>&quot;Fediverse&quot; can describe the world of federated social networks and platforms. Think platforms like Facebook (social media), Twitter (micro-blogging), YouTube (video sharing), Medium (blogs and articles), WhatsApp (instant messaging), etcetera.</p>
<p>A federated platform is one where an <strong>instance</strong> of said platform can interact and interconnect with other instances of the same platform, or instances of another federated platform.</p>
<p>While each instance of the platform could function independently as a full-fledged social platform, at the same time, multiple instances communicating with each other act as if it is a single platform. Content. They do this through a process called <strong>Federation</strong>.</p>
<p>If this does not make sense, yet, let's try an example!</p>
<h3 id="can-you-give-an-example%3F" tabindex="-1">Can you give an example?</h3>
<p>Suppose you are using a federated platform, and let us take Mastodon as an example, which is a federated micro-blogging (imitating twitter) platform.</p>
<p>Suppose I sign up on one instance of Mastodon and create an account (yes, there are many instances of mastodon!). Let us assume this Mastodon instance is instance-A.com.I can use Mastodon as expected, posting to my profile, interacting with other users on instance-A.com.</p>
<p>Suppose you want to use Mastodon too, but happened to sign up on instance-B.com, a different instance from the one I signed up on. While you can similarly interact with other users on instance-B, seeing their posts, and they see your posts, and so on, you can actually even interact with me on instance-A! You may search for my profile then find it, you can follow me, comment on my posts and share them, and the fact that our instances are hosted separately is no obstacle.</p>
<h4 id="self-hosted-private-instances-federate-too!" tabindex="-1">Self-hosted Private Instances Federate too!</h4>
<p>In fact, I can even choose to <strong>self-host</strong> my own instance, where I am the only user. I can just install Mastodon on my server and run it, tag it to domain cosmic-mastodon.com, and suddenly I can interact with you on instance-B!</p>
<h4 id="federating-with-other-platforms" tabindex="-1">Federating with Other Platforms</h4>
<p>Suppose I am not a huge fan of the twitter-like micro-blogging. I can decide to instead use another platform, as long as it uses the same protocol (ActivityPub is the most common, and used by Mastodon). Say for example I want video content and decide to use PeerTube, which is modeled after YouTube. I can post my videos onto a PeerTube instance, and you can subscribe to my account from your Mastodon account on instance-B.com without any issue! Say I want a more threaded-like interface, similar to Reddit. You can use KBin or Lemmy, and you can again follow my account on lemmy or even follow an entire lemmy community (similar to a sub-Reddit).</p>
<h2 id="federation%3A-how-do-instances-talk-with-each-other%3F" tabindex="-1">Federation: How do Instances Talk with Each Other?</h2>
<p>The process of different instances of federated platforms communicating with each other to allow users from each instance to interact with each other and exchange each others' content is what is called <strong>Federation</strong>.</p>
<p>This is possible despite the fact that different instances are hosted separately and enjoy an amount of independence and control over themselves. Each instance only directly controls itself.</p>
<p>At the same time, federated platforms usually agree on a <strong>protocol</strong>, or a set of standards for how to communicate with each other (sort of like a language). This allows different instances to be inter-connected despite the separation boundary. They can send content to and from each other, and because they speak the same language (using same protocol), they can integrate this content as if it originated from inside their instance, and give the user the experience of a single inter-connected platform.</p>
<h2 id="what-is-a-federation-protocol-(e.g.-activitypub)%3F" tabindex="-1">What is a Federation Protocol (e.g. ActivityPub)?</h2>
<p>A federation protocol is a set of standards for how an instance of a federated platform can communicate with others to form an inter-connected network. You can imagine it like a &quot;language&quot; that different instances speak, so that they can know what to expect when attempting to communicate with another instance.</p>
<p>As an analogy, suppose you and I want to agree to a <strong>protocol</strong> for our long distance communications. We might agree on the following protocol:</p>
<ul>
<li>For every communication, I will send you a single page via mail.</li>
<li>On top of that page, there will be a big text at the top. That's the title.</li>
<li>On the page, there will be one line that says &quot;sender: [name]&quot; where [name] is someone's name. Thats the name of the person sending you the letter (most likely me!)</li>
<li>There should be a paragraph that starts with &quot;summary:&quot;. Thats the letter's summary.</li>
<li></li>
</ul>
<p>... and so on. Communication protocols are more or less similar, but involve more computing and network concepts. For example, if one federated instance sends a post to another, how would they send the replies or comments to the post? should they expect them to be in the initial communication? what happens if the communication is too large and the network times out? what happens if the user viewing the content is blocked? and so on.</p>
<h2 id="what-is-an-%22instance%22-of-a-federated-platform%3F" tabindex="-1">What is an &quot;Instance&quot; of a Federated Platform?</h2>
<p>An &quot;instance&quot; of a federated platform is exactly what it sounds like. I can go and install a federated platform like lemmy on my server, and have an entire instance of lemmy on my server, with all its features. The only difference between my instance and others is the content thats on it, and what other instances federate with me or block me (more on this below).</p>
<h3 id="instances-are-self-contained" tabindex="-1">Instances Are Self-Contained</h3>
<p>An instance can function entirely on its own. In fact, many platforms allow turning off Federation, and some instances do so. This way, the platform is isolated and does not connect with instances, and it is fully capable of doing so.</p>
<h3 id="instances-control-themselves-independently" tabindex="-1">Instances Control Themselves Independently</h3>
<p>Even with Federation, instances have a great amount of control over themselves. An instance stores and controls its own data, can control its communication with other instances, and can control the underlying resources of an instance. This theoretically allows for:</p>
<ul>
<li>Controlling who gets to sign up and create an account on an instance</li>
<li>Banning or deleting users from their own instance</li>
<li>Banning users on other instances, but only from their own instance</li>
<li>Banning (or de-federating) entire instances from their own instance</li>
<li>Deleting content from their own instance</li>
<li>Scaling up or down hosting resources for better performance or cost savings</li>
</ul>
<h3 id="instances-can't-exert-the-same-control-on-other-instances" tabindex="-1">Instances Can't Exert the Same Control on Other Instances</h3>
<p>While an instance can infleunce other instances indirectly, like de-federating (disallowing communication with another instance), banning users on other instances from their own instance, and so on, control over other instances is very limited:</p>
<ul>
<li>Can't delete posts on other instances from users on other instances</li>
<li>Can't control communication between two other instances</li>
<li>Can't ban users on other instances from their own instance</li>
</ul>
<h2 id="what-does-it-mean-for-a-an-instance-to-%22federate%22-or-%22de-federate%22-with-another-instance%3F" tabindex="-1">What does it mean for a an instance to &quot;federate&quot; or &quot;de-federate&quot; with another instance?</h2>
<p>When an instance &quot;federates&quot; with another, it means it opens up communication with that other instance, allowing content from the other instance to be visible and interact-able from the first instance.</p>
<p>Consequently, when an instance de-federates from another, they cease this communication, making posts from the other instance invisible and uninteract-able from the first.</p>
<h3 id="you-may-still-see-posts-from-a-de-federated-instance" tabindex="-1">you may still see posts from a de-federated instance</h3>
<p>Note that if there are 3 instances, A, B, and C, and both A and B federate with C, but A and B do not federate with each other, it <em>may</em> still be possible for content from B to be seen in A and vice versa. For example, it may be possible that a user from instance A comments on a post from instance C. This could mean the comment sits on instance C, and thus can be visible to instance B as well.</p>
<h3 id="instance-typically-federate-with-all-by-default-(black-list)" tabindex="-1">Instance typically federate with all by default (black-list)</h3>
<p>Most instances typically federate with all other instances by default, and black-list certain instances when necessary. Some do follow white-list federation though, meaning an instance has to be explicitly allowed to federate with.</p>
<h2 id="which-instance-should-i-sign-up-on%3F-does-it-make-a-difference%3F" tabindex="-1">Which instance should I sign up on? Does it make a difference?</h2>
<p>Choosing a first instance is what seems to stop most people from starting. While there is some differences across instances, most of the time, it will not change much, and it is hard to choose a <em>wrong</em> instance. Any instance will theoretically give you access to the entire platform, assuming it is not a widely hated instance that most other instances block.</p>
<p>So what difference does it make? there are a couple of things:</p>
<h3 id="account-sign-up-requirements-vary-across-instances" tabindex="-1">Account Sign-up Requirements Vary Across Instances</h3>
<p>Some instances have open sign ups, others have them completely closed. Some instances require you to fill out a form, others require you to verify via email. Typically larger instances will have more restrictions, as the task of moderating their instance becomes more difficult, and it is an effort to curb spam.</p>
<h3 id="some-instances-have-larger-federation-networks-than-others" tabindex="-1">Some instances have larger federation networks than others</h3>
<p>Although in theory, an instance should have access to all other instances on the network via federation, in practice there are a couple of things preventing this:</p>
<ul>
<li>Your instance has blocked / de-federated with a number of instances</li>
<li>Your instance is blocked / was de-federated from by a number of other instances</li>
<li>Your instance implements whitelist federation. Meaning only explicitly allowed instances are federated with</li>
</ul>
<p>This is usually not a big concern, unless you join a notoriously hated instance that everyone blocked, or you for some reason have an interest in content on those notorious instances.</p>
<p>In general, if there is someone you really want to follow, then you want to make sure your instance federates with theirs.</p>
<h3 id="rules-and-moderation" tabindex="-1">Rules and Moderation</h3>
<p>Different instances will have different rules and moderation styles. Some are really strict, others are fairly lax.</p>
<p>However, instances that are too lax might get blocked by other instances, if they create moderation problems for them.</p>
<h3 id="performance-and-scaling" tabindex="-1">Performance and Scaling</h3>
<p>It costs to host an instance, and when the number of users grow on an instance, scaling issues start to occur. This can sometimes be remediated by increasing resources or optimizations on the platform itself. Smaller instances tend to do much better in terms of scaling.</p>
<h3 id="verdict" tabindex="-1">Verdict</h3>
<p>We should not over complicate this matter, so I think choosing an instance can be done in the following steps:</p>
<ul>
<li>Search the web for a list comparison of instances of the platform you'd like. There is <a href="https://instances.social/list">instances.social for Mastodon</a> and <a href="https://github.com/maltfield/awesome-lemmy-instances">awesome-lemmy-instances for Lemmy</a> for example.</li>
<li>Choose a random instance! some of them give you random ones at the top anyways</li>
<li>Check if it matches your criteria, as it most likely will. Do the rules match your preference? is the sign up process easy enough? Is the federation network sufficient for you? Is the uptime high enough?</li>
<li>Don't aim for popular instances with high number of users. They are less likely to have open registration, and that is to curb their scaling issues.</li>
</ul>
<h2 id="what-are-the-benefits-of-federated-platforms%3F" tabindex="-1">What are the Benefits of Federated Platforms?</h2>
<p><em>I plan to add more details to this answer in the future</em></p>
<p>Why even consider federated platforms? There are multiple reasons!</p>
<ul>
<li>Accessing or Interacting with Content from Any Platform or Client</li>
<li>Removing reliance on single instances of a platform</li>
<li>Resisting takedowns by avoiding single point of failure</li>
<li>Decentralizing control of social platforms</li>
<li>Ability to self-host, allowing users greater degree of control</li>
</ul>
<h2 id="alternatives-to-federated-platforms%3F" tabindex="-1">Alternatives to Federated Platforms?</h2>
<p><em>TO BE ANSWERED LATER</em></p>
<h2 id="conculsion" tabindex="-1">Conculsion</h2>
<p>I hope this article helped answer some of your questions about the Fediverse. I will continue adding to this as I learn more.</p>

    ]]>
      </content>
    </entry>
  
    
    <entry>
      <title>Securing SSH With Passwordless Public Key Authentication</title>
      <link href="https://cosmicbyt.es/posts/ssh-pub-key-auth/"/>
      <updated>2023-06-22T00:00:00Z</updated>
      <id>https://cosmicbyt.es/posts/ssh-pub-key-auth/</id>
      <content type="html">
        <![CDATA[
      <h2 id="introduction" tabindex="-1">Introduction</h2>
<p>When you launch a shell session to a remote computer or server via SSH, the default behavior will prompt you for a password. This is called &quot;Password Authentication&quot;. We can use public ky cryptography to provide us an alternate method for authentication, that can provide us with added security, convenience, and scalability benefits. This method is called &quot;Public Key Authentication&quot;, or &quot;SSH Key Authentication&quot;, and it is the topic of this article.</p>
<h2 id="summary" tabindex="-1">Summary</h2>
<p>We will talk about the motivation behind public key authentication. Why should you use it instead of password authentication? We will then explain what it is and how it works, then go through the setup process (which is fairly easy). We will then end with a discussion on best practices and security considerations.</p>
<p>Feel free to skip around if you only want the how-to.</p>
<h2 id="why-use-public-key-authentication%3F" tabindex="-1">Why Use Public Key Authentication?</h2>
<h3 id="passwords-suck" tabindex="-1">Passwords Suck</h3>
<p>The biggest and most important reason to use SSH keys is that <strong>passwords suck</strong>. Seriously, passwords have a ton of problems and are also annoying to use. You have to make them strong and complex, you have to use different ones for each account and use case, but at the same time you also have to remember them all. It's a bit contradictory, isn't it?</p>
<p>Depending on passwords for authenticating SSH means your security is only as good as your password's strength, uniqueness, and your users' handling of the password.</p>
<p>SSH cryptographic keys are random and longer than any password you will memorize, except you don't have to memorize them!</p>
<h3 id="having-to-type-a-strong-password-every-time-you-authenticate" tabindex="-1">Having to Type a Strong Password Every Time you Authenticate</h3>
<p>Having to type a password every time you authenticate is tedious, especially when the password is made longer and include different types of characters for added strength. While this is more about convenience, many users resort to unsafe methods, such as keeping the password in a non-encrypted note for easy copy-pasting.</p>
<p>While there are work-arounds that make it easier, such as making a password not required for a certain duration of time after it is entered, that does not eliminate the issue entirely, and in some cases may compromise security.</p>
<h3 id="passwords-prone-to-human-error" tabindex="-1">Passwords Prone to Human Error</h3>
<p>We already discussed some of those above. Humans commit a lot of mistakes in favor of convenience when handling passwords. Writing them down on paper where others can see, writing them on notes on shared computers, sending them via unencrypted messages, making them weak, relating them to personal information (like using your dog's name) and so on.</p>
<h3 id="passwords-struggle-at-scale" tabindex="-1">Passwords Struggle at Scale</h3>
<p>Consider the issues that arise due to scale, like in enterprise settings. For example, you have to share the password, and you have to make sure to do it via a secure method, where most people typically do not.</p>
<p>Even methods deemed secure have issues. It only takes one person messing up to compromise the security of the system, and suddenly everyone has to use a new password.</p>
<p>With SSH keys, every person, user, or connecting device can have their own key. Any of these keys can be invalidated without affecting the others.</p>
<h3 id="ssh-keys-allow-secure-automation" tabindex="-1">SSH Keys Allow Secure Automation</h3>
<p>SSH Keys allows for authentication without manual steps. As long as the client already has the right private key in an expected location, running the ssh command can authenticate immediately. This allows for secure automated tasks (including via shell scripts) involving SSH connections, which are often utilized in enterprise settings.</p>
<h3 id="passwords-are-sent-over-to-the-server" tabindex="-1">Passwords are Sent Over to the Server</h3>
<p>This is not a significant concern, as OpenSSH employs state of the art cryptography to secure and authenticate the session, even when using passwords. While it is true that, with password authentication, the client must send the password to the server, the password is strongly encrypted, and it would be highly unlikely for someone to retrieve the password if the connection is intercepted.</p>
<p>Still, it is worth mentioning that with public key authentication, the key used for authentication technically never has to leave your computer, encrypted or not.</p>
<p>How does it work then, if it does not have to be sent over? How does the server know I am who I am? Let's find out in the next section!</p>
<h2 id="how-it-works" tabindex="-1">How it Works</h2>
<p>SSH Public Key Authentication, as evident from its name, uses Public Key Cryptography or Asymmetric Cryptography. This is contrasted with Private Key Cryptography, or Symmetric Cryptography.</p>
<h3 id="symmetric-cryptography" tabindex="-1">Symmetric Cryptography</h3>
<p>Private key cryptography involves the creation of a single (cryptographic) <strong>key</strong> called the private key. This key is usually stored in a file. If you look at it, you'll find it is a string of random characters. You can think of it as one really strong and long password that you don't have to memorize.</p>
<p>This key is used to encrypt data (whether it is messages, files, passwords, etc), but it is also the same key that can decrypt it. This is why it is called <strong>symmetric encryption</strong>. The same key is used for both encryption and decryption.</p>
<h3 id="public-key-cryptography" tabindex="-1">Public Key Cryptography</h3>
<p>Public key cryptography, on the other hand, usually involves a pair of two keys: One key is called the private key, the other is the public key. The private key must be kept secret at all costs; keeping it secret is detrimental to the mechanism's security. Whereas exposing the public key is not as detrimental, and sometimes poses insignificant risks depending on the situation.</p>
<p>In public key cryptography, if a message is encrypted via one of the key pair, it can only be decrypted by the other key, and vice versa. For example, if we encrypt a message using the private key, we cannot decrypt that message even with the private key. But we can decrypt it using the public key, and only with the public key.</p>
<h4 id="encryption-can-prove-identity" tabindex="-1">Encryption can Prove Identity</h4>
<p>While encryption can be used to conceal and secure the content of a message, it can also be used to prove identity. This is sometimes called a <strong>Cryptographic Signature</strong> or <strong>Digital Signature</strong>. How does it work exactly?</p>
<p>Suppose I create a cryptographic public-private key pair (we will see how to do this later below). I give you the public key, while I keep the private key. Then, suppose I send you a message I encrypted using the private key. You decrypt it successfully, and are able to read it. Yay! But wait. What about proving identity?</p>
<p>Well, there is something else that happened here. The fact that you were even <strong>able to</strong> decrypt the message proves something: this message was encrypted using the private key. Not any private key, but the one that corresponds to the public key I gave you! This proves to you that this message came from me, and only me (or, possibly, someone who stole my private key).</p>
<p>Alternatively, suppose someone else sends you a message trying to pretend to be me. If they do not have my private key, then when you attempt to decrypt the message via the public key that I gave you, then it will fail to give you a readable message. This will prove to you that whoever sent this message is most likely not me.</p>
<p>This is crucial, because SSH uses this characteristic of asymmetric cryptography for authentication. It is a way for a client to prove they are who they are; a client that is granted access to SSH into the system.</p>
<h3 id="how-ssh-public-key-authentication-works" tabindex="-1">How SSH Public Key Authentication Works</h3>
<p>When using public key cryptography with SSH, first of all, we have to have our key pair as we discussed earlier.</p>
<p>The public key must be stored on the remote server that we would connect to or SSH into. This server may have several public keys belonging to more than one entity that may attempt to SSH into it.</p>
<p>The client holds the private key, where it will use it to prove its identity to the server when it attempts to connect. If the private key on the client matches with the public key on the server, then it should succeed. But how does it happen exactly?</p>
<p>The exact mechanism varies by protocol, which can be configured. But here is an example of how it commonly happens.</p>
<p>When the client initiates the SSH connection with the server, initially, the two machines create the initial handshake to calculate a secret shared key. This key does not grant the client access yet, but is used to secure the following communications.</p>
<p>Assuming the right configurations are set (discussed further down), the client creates a message based on the session ID, and signs it with the private key it has. It then sends both the signed message (but without session ID) and the corresponding <strong>public key</strong> to the server (alongside other data like username, protocol, etc).</p>
<p>On the server side, the supplied public key is checked to verify if it is in the authorized list of public keys for the supplied username. Then, the server prepends the session ID back to the signed message, and attempts to verify the signature. If that is successful, the server grants the client access, and a shell session is launched!</p>
<h2 id="using-ssh-public-key-authentication" tabindex="-1">Using SSH Public Key Authentication</h2>
<h3 id="generating-ssh-key-pair" tabindex="-1">Generating SSH Key Pair</h3>
<p>On the client (the device that will initiate the SSH connection to the server), use <code>ssh-keygen</code> to generate a SSH key pair. You can use the <code>-t</code> option to specify the type of key generated. The default is <code>rsa</code>, but <code>ed25519</code> is another recommended alternative.</p>
<pre><code>nezar@cosmicbytes ~ $ ssh-keygen -t ed25519
</code></pre>
<p>You'll get the following output, asking you to choose the file where the key will be saved. If you hit enter, it will save it in the default path it specifies.</p>
<pre><code>Generating public/private ed25519 key pair.
Enter file in which to save the key (/home/nezar/.ssh/id_ed25519): 
</code></pre>
<p>It will then ask you to enter an optional passphrase. The passphrase provides added security by encrypting the key and making it only accessible by entering the passphrase, but it does require you to type it whenever you initiate SSH connection. If you do not wish to use a passphrase and keep things passwordless, leave it empty and just hit Enter.</p>
<pre><code>Enter passphrase (empty for no passphrase):
Your identification has been saved in /home/nezar/.ssh/id_ed25519
Your public key has been saved in /home/nezar/.ssh/id_ed25519.pub
</code></pre>
<p>Now we have our key pair generated!</p>
<h3 id="adding-the-public-key-to-the-server's-authorized-list" tabindex="-1">Adding the Public Key to the Server's Authorized List</h3>
<p>Next, we need to tell the server that if they ever get a SSH client attempting to authenticate with the private key we just generated, that they should accept it. To do this, we must first add the public key that we generated on the client to the server's authorized list.</p>
<h4 id="using-ssh-copy-id" tabindex="-1">Using <code>ssh-copy-id</code></h4>
<p>If you can already SSH to the server from the host (presumably via password), this can be automated using the <code>ssh-copy-id</code> utility. You can use it just like you would use the <code>ssh</code> command, but instead of it launching a shell session, it sends the public key to the server and adds it to its authorized list.</p>
<pre><code>nezar@cosmicbytes ~ $ ssh-copy-id [user]@[server_address]
</code></pre>
<p>Replace <code>[server_address]</code> with the address to the server, and <code>[user]</code> with the remote user name on that server that you wish to authenticate SSH sessions with (remove the square brackets [] ).</p>
<p>If the server address is 192.168.1.50 and the user is &quot;maple&quot;, then the command would be:</p>
<pre><code>nezar@cosmicbytes ~ $ ssh-copy-id maple@192.168.1.50
</code></pre>
<h4 id="manually" tabindex="-1">Manually</h4>
<p>If you are unable to connect to the server via SSH, you can attempt to transfer it manually. To do this, transfer the <strong><strong>public key file ending with .pub</strong></strong> using your desired, secure transfer method to the server. The file should be found at the path <code>/home/[your_user]/.ssh/</code> (where <code>[your_user]</code> is your user name on the client). Make sure not to transfer or touch the private key (the file that does not end with <code>.pub</code>), as exposing it could compromise your security.</p>
<p>After transferring the file to the server using your preferred method, launch a terminal or shell session on the server using the user you want the client to gain SSH authorization on.</p>
<p>Check if the home directory of the user has the <code>.ssh</code> directory. If not, make sure to create it:heart with the proper permissions.</p>
<pre><code># make sure you're in home directory
maple@server ~ $ cd ~
# check to see if .ssh directory exists
maple@server ~ $ test -d &quot;.ssh&quot; &amp;&amp; echo &quot;.ssh dir exists!&quot; || echo &quot;.ssh dir doesn't exist&quot;
# create the directory if it does not exist
maple@server ~ $ mkdir .ssh
# set the right permissions
maple@server ~ $ chmod 700 .ssh
</code></pre>
<p>Add the content of the public key to the <code>authorized_keys</code> file under the <code>.ssh</code> directory. Make sure to append so as to avoid unintentionally erasing previous entries in there.</p>
<pre><code>maple@server ~ $ cat path/to/key.pub &gt;&gt; ~/.ssh/authorized_keys
# make sure the right permissions are set on the file
maple@server ~ $ chmod 600 ~/.ssh/authorized_keys
</code></pre>
<p>And now we have our SSH public key authentication working! If you face any issues, restart ssh daemon to make sure changes took affect, or restart the machines altogether. Then you can attempt to SSH and observe our new authentication method in action.</p>
<h3 id="disabling-password-authentication" tabindex="-1">Disabling Password Authentication</h3>
<p>You can disable password authentication entirely on the server for enhanced security. This way, only SSH keys can be used. To do this, we need to set the <code>PasswordAuthentication</code> option in <code>sshd_config</code> to <code>no</code>.</p>
<p>Open the file <code>/etc/ssh/sshd_config</code> in your preferred text editor and add the following line:</p>
<pre><code>PasswordAuthentication no
</code></pre>
<p>You can do this on a per-user basis using the <code>Match User</code> statement:</p>
<pre><code>Match User maple
    Password Authentication no
</code></pre>
<p>Make sure you do this at the end of the file, so that other options are not also restricted to the user <code>maple</code>.</p>
<h2 id="future-considerations-and-best-practices" tabindex="-1">Future Considerations and Best Practices</h2>
<p>We have successfully added Public Key Authentication to our SSH setup. While this comes with both security and convenience benefits, it is not free of pitfalls and cons.</p>
<p>For example, a common pitfall is improper key management. This can especially get difficult as the usage scales, like in an enterprise setting. When you're managing a large number of keys, it is important to keep track of which ones are in use, removing unused ones.</p>
<p>It is also important to ensure that each key is granted a limited set of privileges based on what is needed. Privileges need to be routinely revised to ensure a key's user is not granted more or less permissions than needed.</p>
<p>Key rotation is an important part of key management, and forgetting it is another common pitfall. Just like passwords, keys should be rotated too! This ensures that unused keys or secretly compromised keys are expired and their threats are eliminated.</p>
<p>Use passphrases wisely. Passphrases have a different security model than typical passwords. They are only used for unlocking a key that is only accessible on one device. They can delay an attacker from obtaining the private key from the private key file, as they are now forced to brute force it. Rotating passphrases is also advised.</p>
<p>It may also be worth considering SSH Certificates instead of Keys. They can offer benefits for enterprise scaling, but they are also with their own pros and cons. We will discuss them in a future article.</p>
<p>Thanks for reading!</p>

    ]]>
      </content>
    </entry>
  
    
    <entry>
      <title>Messaging in Distributed Systems (Pub-Sub, Message Queues, etc)</title>
      <link href="https://cosmicbyt.es/posts/messaging-distributed-systems/"/>
      <updated>2023-11-15T00:00:00Z</updated>
      <id>https://cosmicbyt.es/posts/messaging-distributed-systems/</id>
      <content type="html">
        <![CDATA[
      <h2 id="introduction" tabindex="-1">Introduction</h2>
<p>Messaging is a powerful design pattern in distributed systems, allowing us to decouple system components and enforce greater resiliency and reliability in communication between system components. Typically, one of many dedicated messaging technologies (or message-oriented middleware) is used, with common examples being Kafka, RabbitMQ, and managed services like AWS SQS or GCP's Pub-Sub. But messaging is not suitable for all systems and use cases. While it can greatly simplify certain types of distributed systems and solve many problems, using them in the wrong place can over-complicate the system, make it more expensive, and introduce performance bottlenecks. Before using a messaging technology, it is key to understand the tradeoffs it offers.</p>
<p>In this article, we will discuss messaging at a high level, gaining an understanding of what it really is, its capabilities and limitations. While messaging is a broad term, encompassing more specific patterns such as message queuing, publish-subscribe messaging, and data streaming, they share many aspects and key concepts, and we will focus on messaging as a whole. I will discuss those specific patterns and specific technologies in-depth in future articles.</p>
<h2 id="what-is-a-messaging-service" tabindex="-1">What is a Messaging Service</h2>
<p>In distributed systems, a messaging service is a middleware component that handles receiving and sending messages between other components of the system. It is essentially a communication middle man, abstracting away communication between components and simplifying it. It takes messages from one or more system components, and relays it to one or more other components.</p>
<p><code>| Message Producer (A)  |  ----&gt;  | Message Broker |  ----&gt;  | Message Consumer (B) |</code></p>
<h2 id="why-use-messaging%3F" tabindex="-1">Why Use Messaging?</h2>
<p>On a first look, it might seem like messaging is an unnecessary addition to the system, and we are just placing an unnecessary middle-man when we could establish communication directly. Why not just have the message producer send the message directly to the consumer? This question is valid, and for many cases, that's what you should do if the communication between two components is simple. But a message service will typically do a lot more than merely relay messages, such as persisting messages, retrying failures, solving complicated routing patterns, etc. Let's take a deeper look at what it is capable of.</p>
<h3 id="example" tabindex="-1">Example</h3>
<p>Before we dive deeper, let us consider an example where using a message broker is useful. Think of this example as you read the rest of the article, as it may help deepen your understanding.</p>
<p>Suppose we have a video sharing platform (similar to TikTok). After the video is uploaded, we want to run the video through our community guideline scanner. We decided to do this asynchronously. So after the video is uploaded, we post a message to our message broker, and eventually, this message gets fed into our community guideline scanner.</p>
<p>This is not the only way to architect this, but we take this approach as an example. We also assume that the community guideline scanner can be a long running or CPU-intensive process.</p>
<p><code> Video Uploader ----&gt; Message Queue ----&gt; Community Guidelines Scanner</code></p>
<p>Now let us dive deeper into what a message broker can possibly do for us.</p>
<h3 id="decoupling-system-components" tabindex="-1">Decoupling System Components</h3>
<p>By using a messaging middleware, services A and B are now decoupled, and the contract between them is simplified. Services A and B can each focus on their own tasks, and service A only has to concern itself with sending a message to the message middleware, while service B only has to concern itself with reading or receiving a message. Beyond that, the services are now decoupled and are not concerned with the details or implementation of the other. They only have to agree on the message format or protocol. As per our example, the video uploader is no longer concerned with the Community Guidelines Scanner. It only needs to focus on video uploading and sending the message to the messaging middleware.</p>
<p>&quot;What do you mean they don't have to concern themselves with other details or implementation? I don't use messaging middleware and I have never had to deal with that!&quot;. Bear with me, I discuss that in the remaining points.</p>
<h3 id="asynchronous-workflows" tabindex="-1">Asynchronous Workflows</h3>
<p>If service A wants to call service B to continue execution, but it can be asynchronous, we acquire many benefits using a message middleware. Service A can just send a message to the message middleware, and now its free to process other requests, while the middleware handles telling B to process the remainder of the workflow asynchronously, or merely stores the message until B asks for it. Service A no longer has to concern itself with whether B has received the message or not, or when it is done processing it. This is delegated to the message middleware.</p>
<blockquote>
<p>Following our example, as we said earlier, the video uploader does not have to deal with the community guidelines scanner or even delivering the message to it successfully. We do not have to wait for the long running process. We are immediately free to handle more video uploads again.</p>
</blockquote>
<h3 id="handling-retries" tabindex="-1">Handling Retries</h3>
<p>When one service A has to communicate or call another service B, any failures in service B may lead to loss of data or workflow breakages. Without a message middleware, service A would have to be concerned with making sure the delivery of the message to service B was successful, and that service B was executed successfully. It is often preferable to free service A to process other requests, and offload this task to a message middleware to handle it. A message middleware is able to retry delivering a message until it is processed successfully. It is also capable of ensuring that retrying failures does not impact other traffic negatively.</p>
<blockquote>
<p>Following the example, we would not want a failure in the guidelines scanner to not be retried. But we also do not want the video uploader to handle that. A message broker will take over handling retries.</p>
</blockquote>
<h3 id="prioritization-and-ordering" tabindex="-1">Prioritization and Ordering</h3>
<p>Some message middlewares allow for prioritizing the consumption of messages based on a specific ordering algorithm. Sometimes, an ordering scheme may be as simple as processing messages in the order they were received,  &quot;First In First Out&quot;, or something more complex, like sorting by priority assigned by the message producer.</p>
<blockquote>
<p>In our example, consider if we implemented prioritization. Maybe we want videos coming from users with a record of guideline violations to have higher priority in being scanned. Our message broker can ensure that higher priority videos will get scanned first.</p>
</blockquote>
<h3 id="routing" tabindex="-1">Routing</h3>
<p>A service might be seeking to communicate with many, sometimes an undefined or changing number of receivers. This can complicate the service, and it can be simpler to instead send a message to a message broker and delegate the task of communication to it. The message broker can then handle delivering the message to the right receivers. Receivers can even pull the messages, which prevents from having to keep track of receivers. This means that messaging can greatly simplify communication routing. An almost-identical use case is a receiver wanting to receive messages from an undefined or changing number of senders.</p>
<p>Moreover, when operating at scale, there may be a changing number of instances of a given service as it scales up or down. While there are other solutions to this, a message broker can simplify this task as we just explained.</p>
<h3 id="deduplication" tabindex="-1">Deduplication</h3>
<p>Message middlewares can often handle deduplicating messages. If the same message was sent multiple times, but it is necessary to process a message only once, deduplication can be enacted. Moreover, a message middleware can ensure that a received message is relayed to only one consumer, and only once (or only until successfully processed once). This way, we prevent duplicate processing of a message.</p>
<blockquote>
<p>Following our example, we would not want to waste compute resources scanning the same video twice, since scanning may be expensive.</p>
</blockquote>
<h3 id="handling-traffic-spikes" tabindex="-1">Handling Traffic Spikes</h3>
<p>One use case where message middlewares can be quite helpful is when handling traffic spikes, especially if they're unpredictable and we are working with long-running or intensive processes or asynchronous workflows. By using a message middleware, we can queue and persist the messages until they are processed, without worrying about not having enough instances up to handle our traffic.</p>
<h3 id="scaling-independently" tabindex="-1">Scaling Independently</h3>
<p>By decoupling system components, not only have we separated concerns, but we can allow the services to scale independently from each other. One service might be long running and intensive, and having larger scaling requirements.</p>
<h3 id="persisting-messages-for-offline-consumers" tabindex="-1">Persisting Messages for Offline Consumers</h3>
<p>Imagine if, in our example, the guidelines scanner went down. We would still want to scan all these videos when it comes back up. A message broker can persist these messages long term, until they are consumed. This is similar to how if you were offline for a while, you would still receive messages on your phone that were sent while you were offline, once you come back online.</p>
<h2 id="limitations-of-messaging-middleware%3A-when-not-to-use-them%3F" tabindex="-1">Limitations of Messaging Middleware: When NOT To Use Them?</h2>
<p>While messaging can be a worthy addition to your system, it is not without its setbacks and limitations. In general, it is rare for an addition to a distributed system to not come with its hurdles. Let us discuss them:</p>
<h3 id="limitations-of-most-additions-to-a-distributed-system" tabindex="-1">Limitations of Most Additions to a Distributed System</h3>
<p>The following applies to many distributed system components. I will not discuss them in detail, as they deserve their own discussion:</p>
<ul>
<li>Over-complicating the system</li>
<li>Increasing cost</li>
<li>Incurring latency costs: A message has to travel through greater distances and more components</li>
<li>Performance costs</li>
<li>Introducing extra points of failure that have to be maintained</li>
<li>Introducing more paths for the application flow to be kept track of</li>
<li>More components for which we need to configure, and unless is a managed service, we have to manage and maintain its infrastructure</li>
</ul>
<h3 id="synchronous-workflows" tabindex="-1">Synchronous Workflows</h3>
<p>A message middleware is unsuitable for synchronous workflows. It is often the case that a service A invokes a service B synchronously. This could be because service A uses the response from B to continue its processing, or it could be because service A handles the response to a client as part of a request-response cycle.</p>
<p>Message middlewares are great for asynchronous workflows, and often times, it might be beneficial to convert your synchronous workflow into asynchronous,either partially or completely. But this is not always possible, and one has to evaluate the benefits of such conversion</p>
<h3 id="latency" tabindex="-1">Latency</h3>
<p>Although latency increases are common to many distributed system components, many (but not all) message middlewares may incur greater latency costs than others.</p>
<p>In general, a message middleware will often persist the message rather than merely route it. A message received by the middleware may take time to become available for consumption or processing. The middleware has to first persist the message, and a distributed message middleware may have to replicate the persisted message to multiple clusters.</p>
<p>This is why most message middlewares are used in asynchronous workflows.</p>
<p>However, it is important to point out that this does not apply to all message middlewares. Some are designed to handle stream processing at incredibly low latency, but still garner the benefits of the messaging pattern. I will discuss this in a future article about Apache Kafka.</p>
<h2 id="conclusion" tabindex="-1">Conclusion</h2>
<p>Let us revise our definition of messaging in distributed systems. Recall that a message middleware is a service that acts as a communication middle-man between two or more system components. Other system components can send messages to the messaging service, or receive (or pull) messages from it.</p>
<p>But this was not sufficient to truly grasp the power of messaging. A messaging service abstracts away the complications of communication in distributed systems, and allows system components to simplify communication by only sending or receiving messages. The message middleware can handle retrying failures, persisting messages, and simplify routing communication to multiple receivers or instances of a receiver. It can allow the system to scale better, be more reliable, and prevent error states.</p>
<p>Before using a message broker, remember that they are not always trivial to setup and maintain, and they may be adding performance bottlenecks to your system. Make sure to consider the tradeoffs before putting in the effort!</p>

    ]]>
      </content>
    </entry>
  
    
    <entry>
      <title>The Finite State Machine</title>
      <link href="https://cosmicbyt.es/posts/finite-state-machine/"/>
      <updated>2024-01-06T00:00:00Z</updated>
      <id>https://cosmicbyt.es/posts/finite-state-machine/</id>
      <content type="html">
        <![CDATA[
      <h2 id="introduction" tabindex="-1">Introduction</h2>
<p>The Finite State Machine is a conceptual <a href="https://en.wikipedia.org/wiki/Model_of_computation">model of computation</a> known for its simplicity. Although it has significance in the study of <a href="https://en.wikipedia.org/wiki/Automata_theory">automata</a> and <a href="https://en.wikipedia.org/wiki/Theory_of_computation">computation theory</a>, it also has practical significance, such as its application in lexical analysis, text parsing, user interfaces, event-driven systems, machine learning, and others. They are found around us in every day code and can usually be used to improve or simplify your every day code. You probably have written a finite state machine without even knowing it!</p>
<h2 id="what-is-it-a-finite-state-machine%3F" tabindex="-1">What is it a Finite State Machine?</h2>
<p>A Finite State Machine is a type of model of computation, which is an <a href="https://en.wikipedia.org/wiki/Automaton">abstract machine</a> that performs computation and solves problems via algorithms. What makes it stand out to other models of computation is that it can only be in one state at a time, out of a finite (limited) number of pre-defined states. The machine takes an input, reading its elements one-by-one (think characters of a word or string), and then changes its state based on the input element. The machine has a <strong>transition function</strong>, which tells it how to change its state based on its input. In other words, the machine changes state according to its transition function.</p>
<blockquote>
<p>We study abstract machines instead of real ones to simplify our studies. The abstract machines are close enough to abstract real world cases, but forgetting about real world restrictions simplifies our model and helps us reason about the model and arrive to conclusions more easily.</p>
</blockquote>
<p><strong>Why limit ourselves to such a basic model</strong> when we have such powerful computing machines? Well it turns out that this simple model bear properties that we would not have using a more powerful model, like the Turing Machine or the RAM model found in every day computers.</p>
<p>To understand this abstract talk better, let's take an example.</p>
<h2 id="example%3A-detecting-consecutive-errors" tabindex="-1">Example: Detecting Consecutive Errors</h2>
<p>We have an e-commerce site that allows customers to place orders. We have received complaints about the site's performance, and we suspect the search end-point is the offending one. We tolerate one-off errors occurring due to network conditions, but two or more errors in a row are not. We want to investigate logs of the search end-point to see if it has had two or more errors in a row.</p>
<p>We have a list of all our application logs over the past month to scan for consecutive errors. We can write an algorithm using a typical programming language to do this. Below is a Python example:</p>
<h3 id="imperative-code-solution" tabindex="-1">Imperative Code Solution</h3>
<pre class="language-python"><code class="language-python"><span class="token keyword">def</span> <span class="token function">are_errors_consecutive</span><span class="token punctuation">(</span>logs<span class="token punctuation">)</span><span class="token punctuation">:</span><br>    consecutive_errors <span class="token operator">=</span> <span class="token number">0</span><br>    <span class="token keyword">for</span> log <span class="token keyword">in</span> logs<span class="token punctuation">:</span><br>        <span class="token keyword">if</span> is_error_log<span class="token punctuation">(</span>log<span class="token punctuation">)</span><span class="token punctuation">:</span><br>            consecutive_errors <span class="token operator">+=</span> <span class="token number">1</span> <span class="token comment"># increment by one</span><br>        <span class="token keyword">else</span><span class="token punctuation">:</span><br>            consecutive_errors <span class="token operator">=</span> <span class="token number">0</span> <span class="token comment"># reset back to 0</span><br>        <span class="token keyword">if</span> consecutive_errors <span class="token operator">>=</span> <span class="token number">2</span><span class="token punctuation">:</span><br>            <span class="token keyword">return</span> true<br>    <span class="token keyword">return</span> false</code></pre>
<p>We first initialize a variable <code>consecutive_errors</code> then start a for-loop through the input. The variable holds our state throughout the loop. For each log, we check if it is an error, and update the state variable by incrementing it if an error was found, or resetting to 0 if non-error log is encountered. If we see that there have already been two consecutive errors, we return early from the function.</p>
<blockquote>
<p>We used <code>is_error_log()</code> since we did not specify what our input looks like exactly. Its implementation depends on the form of the input.</p>
</blockquote>
<p>The program we just wrote is very similar to a finite state machine! Let us explore the finite state machine implementation.</p>
<h3 id="finite-state-machine-solution" tabindex="-1">Finite State Machine Solution</h3>
<p>A finite state machine can only be in one state. Thankfully, our function already used one state variable with only a finite number of possibilities: 0, 1, and 2 errors detected.</p>
<p>The state has two values of special interest: the starting state and acceptance state. Our starting state is 0, and acceptance is 2 consecutive errors detected. If execution ends with the machine in the accept state, then the input altogether is accepted. That is the &quot;return&quot; of the finite state machine: whether the input is accepted or not.</p>
<blockquote>
<p>Real world implementations of the finite state machine may deviate slightly from these definitions. For example, we may have the final state as the return value rather than a binary accept or reject. Those machines would still closely resemble the abstract finite state machine.</p>
</blockquote>
<h4 id="transition-function" tabindex="-1">Transition Function</h4>
<p>An important part of the state machine is how it moves from state to another. The finite state machine reads the elements of the input one-by-one. For every element, it may change its state based on the input element and its current state. This is called the &quot;transition function&quot;, defining how the machine changes state.</p>
<p>In our example, we can describe the transition function as follows:</p>
<pre><code>- If state is 0, and non-error log encountered, new state is 0
- If state is 0, and error log encountered, new state is 1
- state = 1, input = non-error -&gt; state = 0
- state = 1, input = error -&gt; state = 2
- state = 2, input = non-error -&gt; state = 2
- state = 2, input = error -&gt; state = 2
</code></pre>
<p>One last difference about the finite state machine is that it does not return early. It will read the input in its entirity until it is done.</p>
<p>Congratulations, we just devised a finite state machine! Let us walk through a sample execution.</p>
<h4 id="sample-execution-of-finite-state-machine" tabindex="-1">Sample Execution of Finite State Machine</h4>
<p>Let's take an example input. For simplicity, let us define a non-error log as 0, and an error log as 1. Then, let us take the following input example: <code>00100110</code><br>
</br></p>
<ul>
<li>The machine starts with its starting state as 0 (state = 0)</li>
<li>it reads the first input element as 0. Based on the transition function, the new state remains at 0 (state = 0)</li>
<li>same as above happens for the second input element, as it is also a 0 (state = 0)</li>
<li>the third input = 1. Based on transition function, state moves to 1. (state = 1)</li>
<li>input = 0. Based on transition function, state moves to 0 (state = 0)</li>
<li>input = 0, new state = 0 (state = 0)</li>
<li>input = 1, new state = 1 (state = 1)</li>
<li>input = 1, new state = 2 (state = 2)</li>
<li>input = 0, new state remains at 2 (state = 2)</li>
</ul>
<p>we Finish execution on an accept state, so our input is accepted!</p>
<blockquote>
<p>Notice how it is easier to reason about the algorithm by thinking of it as a bunch of states and transitions between them, rather than an imperative loop. While imperative loops are a common encounter for programmers and make a lot of sense, state machines are even easier to make sense of for non-technical people. All it takes is thinking of states and transitions. The difference in ease of understanding may be more apparent in more complex problems compared to our simple example.</p>
</blockquote>
<h2 id="definition-of-finite-state-machine" tabindex="-1">Definition of Finite State Machine</h2>
<p>To define a specific finite state machine, we must define the following:</p>
<ul>
<li>** An Alphabet:** The range of values it accepts as input elements</li>
<li>** States:** The finite list of possible states the machine can hold</li>
<li>** Starting State**</li>
<li>** Accept State**</li>
<li>** Transition Function:** Defines how the machine changes state, given an input element and current state as input to the function</li>
</ul>
<p>Our example above can be defined as follows:</p>
<ul>
<li>** An Alphabet:** The set <code>[&quot;error log&quot;, &quot;non-error log&quot;]</code></li>
<li>** States:** <code>[0,1,2]</code></li>
<li>** Starting State:** 0</li>
<li>** Accept State:** 2</li>
<li>** Transition Function:** We already defined it above in the transition function section</li>
</ul>
<p>A deterministic finite state machine bearing the above is a machine that holds a single state, and reads an input, changing state based on its transition function as it goes.</p>
<p>What we listed above are all the constraints needed to define a Finite State Machine.</p>
<h2 id="you-have-built-finite-state-machines-without-noticing!" tabindex="-1">You Have Built Finite State Machines Without Noticing!</h2>
<p>Notice how the example we presented was originally solved using a traditional loop in the common imperative style of code. If you're a programmer, you have probably written many Finite State Machines without even noticing that they are! This is one of the important aspects of learning models of computation. It is not only about choosing which model of computation to use, but also about identifying them, which can help with conceptualizing and visualizing, as well as recognizing the properties you can apply to them.</p>
<h2 id="characteristics-of-finite-state-machines" tabindex="-1">Characteristics of Finite State Machines</h2>
<h3 id="limited-memory-and-good-performance" tabindex="-1">Limited Memory and Good Performance</h3>
<p>The &quot;finite state&quot; part means that the machine's memory is bounded. This is in contrast to many other models of computation where memory is unbounded. This makes the Finite State Machine very performant in many cases, and useful in certain low level programming such as embedded devices and network devices.</p>
<p>This also means that the Finite State Machine is unsuitable for applications where finite memory is insufficient. For example, consider the problem of verifying whether an input has a certain number of 0's followed by the same exact number of 1's. This is impossible for a Finite State Machine to solve, as it requires unbounded memory, counting an unbounded number of 0's in order to verify that the number of 1's is equal.</p>
<blockquote>
<p><strong>Note:</strong> nearly all real world problems have some memory bound, even if unrealistically high. So you can still use Finite State Machines there, but it just may not be the best idea to do so.</p>
</blockquote>
<h3 id="simplicity" tabindex="-1">Simplicity</h3>
<p>The simplicity of the Finite State Machine makes it easy to reason about. Modeling problems using it can help you identify superfluous or error states, use reduction methods to simplify the problem, or make use of the other characteristics we discuss below.</p>
<h3 id="easy-to-construct-complex-machines-from-simpler-ones" tabindex="-1">Easy to Construct Complex Machines from Simpler Ones</h3>
<p>Every Finite State Machine can be expressed as a combination of simpler ones. In fact, Every one of them can be broken down to the most basic Finite State Machines, i.e. Machines that accept if the input is one specific character, and reject otherwise.</p>
<p>If we take our error log example, the machine we devised can be thought of as the combination of two simpler machines: one that accepts 0's only, and one that accepts two or more 1's only (no 0's). Those two machines can in turn be broken down further into elementary machines: one that accepts the single input 0 and rejects everything else, and the same but for 1.</p>
<p>This has important implications, one of which probably affects your every day programming life: Regular Expressions.</p>
<h4 id="regular-expressions" tabindex="-1">Regular Expressions</h4>
<p>Regular expressions are actually nothing more than expressing the class of inputs that a Finite State Machine must accept, but using elementary characters. For example, if I have the regular expression <code>/yes/</code>, this can be thought of as the combination of <code>/y/</code>, <code>/e/</code>, and <code>/s/</code>, or the finite state machines that recognize each of those individually. This is called the &quot;concat&quot; operation. The union operation can be expressed as <code>/[abc]/</code> which would accept an input that is &quot;a&quot;, &quot;b&quot;, or &quot;c&quot;.</p>
<p>Regular expressions are indeed built on top of regular expressions, at least conceptually (modern implementations tend to have extensions).</p>
<h3 id="equivalence-of-deterministic-and-non-deterministic-finite-state-machines" tabindex="-1">Equivalence of Deterministic and Non-Deterministic Finite State Machines</h3>
<p>Most of what we have seen is about deterministic Finite State Machines, in which transition functions have deterministic outcomes. That is, given a current state and an input, there is always one possible non-null state outcome. Moreover, every state has a transition for every input-state combination.</p>
<p>Non-deterministic Finite State Machines do not have this requirement. In other words, a transition could have one or more output states for the same input. How does this make sense? well, when multiple outputs are encountered, you can imagine as if the process splits, with two new state machine processes born out of it, one for each state. Execution continues as normal, and one of these &quot;processes&quot; ending in an accept state is sufficient for the entire input to accepted. If this does not make sense, I will be writing a separate blog post on Non-Deterministic Finite State Machines with examples, so stay tuned! Feel free to check the <a href="https://en.wikipedia.org/wiki/Nondeterministic_finite_automaton">Wikipedia article</a> in the meantime.</p>
<p>The key takeaway though: it may seen that Non-Deterministic FSMs (Finite State Machine) are more powerful and capable of solving more problems than their deterministic counterpart. In reality, every Non-Deterministic FSM has an equivalent deterministic FSM. Not only that, there is a specific mechanism for converting a Non-deterministic FSM to a deterministic one.</p>
<p>This is quite important. Some problems are much easier to conceptualize with a non-deterministic model, but can be easily converted to a deterministic model, conserving the simplicity of the latter.</p>
<h3 id="the-pumping-lemma" tabindex="-1">The Pumping Lemma</h3>
<p>Although out of scope for this article, regular languages have an important mathematical property that they all share called the <a href="https://en.wikipedia.org/wiki/Pumping_lemma_for_regular_languages">Pumping Lemma</a>. The key takeaway is that this makes it possible to mathematically prove whether a language (set of inputs) is regular (solvable by finite state machine) without having to search for a specific implementation.</p>
<h2 id="applications-of-finite-state-machines" tabindex="-1">Applications of Finite State Machines</h2>
<p>Although important in theory, Finite State Machines have practical applications. They are commonly used in lexical parsing, network devices, machine learning and others.</p>
<p>Moreover, as we saw earlier, you might write a program that is a Finite State Machine without noticing it! You don't have to go out of your way to write programs as FSMs to gain their benefits. Sometimes, it is enough to recognize them in existing problems and solutions. It'll make it easier to map the program's state to the real-world representation. You might represent state and its transitions in a clearer, more maintainable and readable way. Moreover, being more intentional about state management will help you avoid errors in the long run.</p>
<p>Some examples that you are more likely to run into day-to-day:</p>
<ul>
<li><strong>Event-Driven Programming:</strong> Event driven programming is increasingly common in distributed systems, and they can often be modeled as state machines</li>
<li><strong>Reactive UI State:</strong> If you have ever created a React.JS component, you very likely modeled your component state as a state machine</li>
<li><strong>Regular Expressions (Regex):</strong> Explained in a previous section</li>
</ul>

    ]]>
      </content>
    </entry>
  
    
    <entry>
      <title>Reactivity and Reactive Programming</title>
      <link href="https://cosmicbyt.es/posts/reactivity-and-reactive-programming/"/>
      <updated>2024-07-04T00:00:00Z</updated>
      <id>https://cosmicbyt.es/posts/reactivity-and-reactive-programming/</id>
      <content type="html">
        <![CDATA[
      <h2 id="introduction" tabindex="-1">Introduction</h2>
<p>Reactive programming has become increasingly popular in the frontend programming world, with the advent of modern browser UI frameworks such as React and Vue, nearly all of which boast &quot;Reactivity&quot; as a core feature.</p>
<p>While it is popular in the web browser world, it is not inherent to it. Reactive programming is a general programming paradigm that can be applied anywhere, without a UI involved. So if you are not a frontend developer, do not be discouraged!</p>
<p>In my opinion, reactive programming is what throws off many backend developers when they attempt frontend programming. Reactive programming can be confusing to the newcomer, as it completely changes the flow of a program. But once you get familiar with it, it can unlock powerful programming patterns and change the way you write your program.</p>
<h2 id="what-is-reactivity%3F" tabindex="-1">What is reactivity?</h2>
<p>Reactivity is a declarative programming paradigm in which changes propagate across <strong>layers of data</strong> automatically and implicitly. It is declarative because the programmer does not have to specify how to propagate data changes and keep them in sync.</p>
<p>Does not make sense yet? Let us demonstrate with examples!</p>
<h2 id="example%3A-spreadsheets" tabindex="-1">Example: Spreadsheets</h2>
<p>If you are not familiar with spreadsheet software such as Microsoft Excel, skip this section.</p>
<p>Spreadsheet software such as Microsoft Excel have a feature similar to reactivity, so let us check it out. Suppose you have a list of expenses that you are splitting up with some friends. You want to know how much you have spent on each expense. Your spreadsheet could look like this:</p>
<table>
<thead>
<tr>
<th>Expense Name</th>
<th>Cost</th>
<th>My Share</th>
<th>Number of Friends</th>
</tr>
</thead>
<tbody>
<tr>
<td>Restaurant</td>
<td>70</td>
<td>14</td>
<td>5</td>
</tr>
<tr>
<td>Transportation</td>
<td>30</td>
<td>6</td>
<td></td>
</tr>
<tr>
<td>Tickets</td>
<td>400</td>
<td>80</td>
<td></td>
</tr>
<tr>
<td>Total</td>
<td>500</td>
<td>100</td>
<td></td>
</tr>
</tbody>
</table>
<p>In  spreadsheet software, you can often have the ability to insert formulas that make calculations for you. For example, we can tell it that the &quot;My share&quot; column equals corresponding (on same row) &quot;cost&quot; divided by &quot;Number of friends&quot; (regardless of row). We can also tell it that &quot;Total&quot; is the sum of column above it.</p>
<p>Although this feature saves us some calculations that would otherwise be performed manually, it also has another important benefit: Data binding. If I change one of the values in &quot;Cost&quot;, the spreadsheet recognizes there is a data dependency and that it must update other data fields. This is because we previously gave it a formula that told it that these data fields are calculated using &quot;Cost&quot;. Suppose we found additional restaurant costs and the total is now 120 instead of 70. We only need to update the &quot;cost&quot; column, and if we did things right, the spreadsheet will automatically update &quot;my share&quot;:</p>
<table>
<thead>
<tr>
<th>Expense Name</th>
<th>Cost</th>
<th>My Share</th>
<th>Number of Friends</th>
</tr>
</thead>
<tbody>
<tr>
<td>Restaurant</td>
<td>120</td>
<td>24</td>
<td>5</td>
</tr>
<tr>
<td>Transportation</td>
<td>30</td>
<td>6</td>
<td></td>
</tr>
<tr>
<td>Tickets</td>
<td>400</td>
<td>80</td>
<td></td>
</tr>
<tr>
<td>Total</td>
<td>550</td>
<td>110</td>
<td></td>
</tr>
</tbody>
</table>
<p>We did not have to explicitly tell the spreadsheet to update these columns. This occurs automatically.</p>
<h2 id="example%3A-code" tabindex="-1">Example: Code</h2>
<p>Suppose I have 3 pieces of data: A, B, C. Think of them as variables holding the data. Now imagine there is a relationship between them. For example: A = B + C. Here is some sample code:</p>
<pre class="language-javascript"><code class="language-javascript"><span class="token keyword">let</span> b <span class="token operator">=</span> <span class="token number">1</span><br><span class="token keyword">let</span> c <span class="token operator">=</span> <span class="token number">2</span><br><span class="token keyword">let</span> a <span class="token operator">=</span> b <span class="token operator">+</span> c <span class="token comment">// a = 3</span></code></pre>
<p>Now suppose the value of C changes:</p>
<pre class="language-javascript"><code class="language-javascript"><span class="token keyword">if</span> <span class="token punctuation">(</span>some_condition<span class="token punctuation">)</span> <span class="token punctuation">{</span><br>    c <span class="token operator">=</span> <span class="token number">4</span><br><span class="token punctuation">}</span></code></pre>
<p>Now, C is 4, B remains at 1, but A also remains at 3. Interesting to note is that the statement <code>a = b + c</code> is no longer true, because if we were to calculate this with the new value of C, we would get 5.</p>
<p>Well that's not a problem, we can just recalculate A again right?</p>
<pre class="language-python"><code class="language-python"><span class="token comment"># --- old code ---</span><br>let b <span class="token operator">=</span> <span class="token number">1</span><br>let c <span class="token operator">=</span> <span class="token number">2</span><br>let a <span class="token operator">=</span> b <span class="token operator">+</span> c <span class="token operator">//</span> a <span class="token operator">=</span> <span class="token number">3</span><br><span class="token keyword">if</span> <span class="token punctuation">(</span>some_condition<span class="token punctuation">)</span> <span class="token punctuation">{</span><br>    c <span class="token operator">=</span> <span class="token number">4</span><br><span class="token punctuation">}</span><br><span class="token comment"># --- new code ---</span><br>a <span class="token operator">=</span> b <span class="token operator">+</span> c</code></pre>
<p>But what if C changes again? we would have to add a = b + c everywhere it happens. Surely this is bad programming. What if we can bind the changes to C to A, such that, if it ever changes, A is recalculated and remains in sync with changes in C? That is essentially reactive programming. Let us demonstrate:</p>
<pre class="language-javascript"><code class="language-javascript"><span class="token comment">// library code</span><br><span class="token keyword">class</span> <span class="token class-name">ReactiveValue</span> <span class="token punctuation">{</span><br>    <span class="token function">constructor</span><span class="token punctuation">(</span>val<span class="token punctuation">,</span> updater <span class="token operator">=</span> <span class="token keyword">null</span><span class="token punctuation">,</span> <span class="token function-variable function">callback</span> <span class="token operator">=</span> <span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token operator">=></span> <span class="token punctuation">{</span><span class="token punctuation">}</span><span class="token punctuation">)</span> <span class="token punctuation">{</span><br>        <span class="token keyword">if</span> <span class="token punctuation">(</span>val <span class="token operator">==</span> <span class="token keyword">null</span><span class="token punctuation">)</span> <span class="token punctuation">{</span><br>            <span class="token keyword">if</span> <span class="token punctuation">(</span>updater <span class="token operator">==</span> <span class="token keyword">null</span><span class="token punctuation">)</span> <span class="token punctuation">{</span><br>                <span class="token keyword">throw</span> <span class="token function">Error</span><span class="token punctuation">(</span><span class="token string">'Cannot have both value and updater as null'</span><span class="token punctuation">)</span><span class="token punctuation">;</span><br>            <span class="token punctuation">}</span> <span class="token keyword">else</span> <span class="token punctuation">{</span><br>                <span class="token keyword">this</span><span class="token punctuation">.</span>val <span class="token operator">=</span> <span class="token function">updater</span><span class="token punctuation">(</span><span class="token punctuation">)</span><br>            <span class="token punctuation">}</span><br>        <span class="token punctuation">}</span> <span class="token keyword">else</span> <span class="token punctuation">{</span><br>            <span class="token keyword">this</span><span class="token punctuation">.</span>val <span class="token operator">=</span> val<br>            <span class="token keyword">this</span><span class="token punctuation">.</span>callback <span class="token operator">=</span> callback<br>            <span class="token keyword">this</span><span class="token punctuation">.</span>updater <span class="token operator">=</span> updater<br>        <span class="token punctuation">}</span><br>    <span class="token punctuation">}</span><br><br>    <span class="token function">getValue</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span><br>        <span class="token keyword">return</span> <span class="token keyword">this</span><span class="token punctuation">.</span>val<br>    <span class="token punctuation">}</span><br><br>    <span class="token function">update</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span><br>        <span class="token keyword">if</span> <span class="token punctuation">(</span><span class="token keyword">this</span><span class="token punctuation">.</span>updater <span class="token operator">!=</span> <span class="token keyword">null</span><span class="token punctuation">)</span> <span class="token punctuation">{</span><br>            <span class="token keyword">this</span><span class="token punctuation">.</span><span class="token function">updater</span><span class="token punctuation">(</span><span class="token punctuation">)</span><br>        <span class="token punctuation">}</span><br>    <span class="token punctuation">}</span><br><br>    <span class="token function">setValue</span><span class="token punctuation">(</span><span class="token parameter">newVal</span><span class="token punctuation">)</span> <span class="token punctuation">{</span><br>        <span class="token keyword">this</span><span class="token punctuation">.</span>val <span class="token operator">=</span> newVal<br>        <span class="token keyword">if</span> <span class="token punctuation">(</span><span class="token keyword">this</span><span class="token punctuation">.</span>callback <span class="token operator">!=</span> <span class="token keyword">null</span><span class="token punctuation">)</span> <span class="token punctuation">{</span><br>            <span class="token keyword">this</span><span class="token punctuation">.</span><span class="token function">callback</span><span class="token punctuation">(</span><span class="token punctuation">)</span><br>        <span class="token punctuation">}</span><br>    <span class="token punctuation">}</span><br><br>    <span class="token function">setCallback</span><span class="token punctuation">(</span><span class="token parameter">callback</span><span class="token punctuation">)</span> <span class="token punctuation">{</span><br>        <span class="token keyword">this</span><span class="token punctuation">.</span>callback <span class="token operator">=</span> callback<br>    <span class="token punctuation">}</span><br><span class="token punctuation">}</span><br><br><span class="token comment">// application code</span><br><br><span class="token keyword">let</span> b <span class="token operator">=</span> <span class="token keyword">new</span> <span class="token class-name">ReactiveValue</span><span class="token punctuation">(</span><span class="token number">1</span><span class="token punctuation">)</span><br><span class="token keyword">let</span> c <span class="token operator">=</span> <span class="token keyword">new</span> <span class="token class-name">ReactiveValue</span><span class="token punctuation">(</span><span class="token number">2</span><span class="token punctuation">)</span><br><span class="token keyword">let</span> a <span class="token operator">=</span> <span class="token keyword">new</span> <span class="token class-name">ReactiveValue</span><span class="token punctuation">(</span><span class="token keyword">null</span><span class="token punctuation">,</span> <span class="token keyword">null</span><span class="token punctuation">,</span> <span class="token keyword">function</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span><br>    <span class="token keyword">return</span> b<span class="token punctuation">.</span><span class="token function">getValue</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token operator">+</span> c<span class="token punctuation">.</span><span class="token function">getValue</span><span class="token punctuation">(</span><span class="token punctuation">)</span><br><span class="token punctuation">}</span><span class="token punctuation">)</span><br><br>c<span class="token punctuation">.</span><span class="token function">setCallback</span><span class="token punctuation">(</span>a<span class="token punctuation">.</span>update<span class="token punctuation">)</span><br><br>b<span class="token punctuation">.</span><span class="token function">setCallback</span><span class="token punctuation">(</span>a<span class="token punctuation">.</span>update<span class="token punctuation">)</span><br><br>console<span class="token punctuation">.</span><span class="token function">log</span><span class="token punctuation">(</span>a<span class="token punctuation">.</span><span class="token function">getValue</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">)</span> <span class="token comment">// 3</span><br>c<span class="token punctuation">.</span><span class="token function">setValue</span><span class="token punctuation">(</span><span class="token number">4</span><span class="token punctuation">)</span><br>console<span class="token punctuation">.</span><span class="token function">log</span><span class="token punctuation">(</span>a<span class="token punctuation">.</span><span class="token function">getValue</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">)</span> <span class="token comment">// 5</span></code></pre>
<p>Now, anytime C and B are updated, A will update, without explicitly specifying how and when to do so every time we needed. Cool, right?</p>
<h2 id="different-implementations" tabindex="-1">Different Implementations</h2>
<p>To achieve reactivity, our library (the <code>ReactiveValue</code> class) needed a way to figure out the dependencies between data. In our implementation, we had to explicitly tell the program which values are reactive and what the dependencies are. Many implementations will attempt to figure out the dependencies automatically. Some may treat all values as reactive values, and others will attempt to automatically figure out which variables are reactive. The point is, do not get stuck on specific implementations, but take it as a way to demonstrate the concept of Reactivity.</p>
<h2 id="conclusion" tabindex="-1">Conclusion</h2>
<p>So let us summarize: What is reactivity?</p>
<p>It is a programming paradigm in which the program automatically and implicitly updates data; in other words, without explicit direction from the programmer on how and when to update the data. Implementations may look different from library to other, and even application code that uses these libraries will look very different. The point is that, from the user's perspective, there is some form of automatic propagation of data changes.</p>
<p>In typical imperative programming style, the program flows step by step, line by line. When we assign a value to a variable, it is said and done. We have the confidence that it will never change unless explicitly changed. In reactive programming, the values may change, but the data relations are preserved. This may be unintuitive to a newcomer, and it may not make sense for certain programs. Use it in the right situations, and you will find it to be powerful and useful.</p>

    ]]>
      </content>
    </entry>
  
</feed>