<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>Forem: Elijah</title>
    <description>The latest articles on Forem by Elijah (@elijahu).</description>
    <link>https://forem.com/elijahu</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F3670432%2F9cae2ea9-8b30-4569-ad43-c973c607e67d.jpg</url>
      <title>Forem: Elijah</title>
      <link>https://forem.com/elijahu</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://forem.com/feed/elijahu"/>
    <language>en</language>
    <item>
      <title>KodeKloud Days 5-8: SELinux and Cron Jobs</title>
      <dc:creator>Elijah</dc:creator>
      <pubDate>Tue, 23 Dec 2025 19:00:00 +0000</pubDate>
      <link>https://forem.com/elijahu/kodekloud-days-5-8-selinux-and-cron-jobs-1p2l</link>
      <guid>https://forem.com/elijahu/kodekloud-days-5-8-selinux-and-cron-jobs-1p2l</guid>
      <description>&lt;p&gt;"Plot twist: Not every Linux is Ubuntu. Also, KodeKloud's UI is testing my patience like a buffering Netflix video at 3%. 📡💀"&lt;/p&gt;

&lt;p&gt;Week two of the 100 Days challenge, and I got hit with a reality check: Ubuntu isn't the only Linux distro. After years of Ubuntu-centric DevOps tutorials, KodeKloud threw CentOS Stream at me and watched me flail.&lt;/p&gt;

&lt;p&gt;Here's what I learned (and broke) this week.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Day 5: SELinux Configuration - The "Wait, This Isn't Ubuntu?" Moment&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;My confident first attempt:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;sudo &lt;/span&gt;apt update
&lt;span class="nb"&gt;sudo &lt;/span&gt;apt &lt;span class="nb"&gt;install&lt;/span&gt; &lt;span class="nt"&gt;-y&lt;/span&gt; selinux-basics
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Result: Command not found.&lt;/p&gt;

&lt;p&gt;Turns out I was on CentOS Stream, not Ubuntu. Different package manager, different everything.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Check your OS FIRST (lesson learned)&lt;/span&gt;
&lt;span class="nb"&gt;cat&lt;/span&gt; /etc/os-release

&lt;span class="c"&gt;# CentOS uses dnf, not apt&lt;/span&gt;
&lt;span class="nb"&gt;sudo &lt;/span&gt;dnf &lt;span class="nb"&gt;install&lt;/span&gt; &lt;span class="nt"&gt;-y&lt;/span&gt; selinux-policy selinux-policy-targeted

&lt;span class="c"&gt;# Edit SELinux config&lt;/span&gt;
&lt;span class="nb"&gt;sudo &lt;/span&gt;vi /etc/selinux/config
&lt;span class="c"&gt;# Change SELINUX=enforcing to SELINUX=disabled&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Why this surprised me: Every DevOps tutorial I've ever done uses Ubuntu. AWS tutorials? Ubuntu. Docker labs? Ubuntu. Kubernetes courses? Ubuntu. So encountering CentOS felt like showing up to a JavaScript class and being handed Assembly code.&lt;/p&gt;

&lt;p&gt;Lesson learned: Always check &lt;code&gt;/etc/os-release&lt;/code&gt; before assuming you know what distro you're on. Package managers are not interchangeable, despite what my Ubuntu-trained muscle memory believes.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Day 6: Cron Jobs - Easier Than Expected&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Set up a cron job to echo "hello" to &lt;code&gt;/tmp/cron_text&lt;/code&gt; every 5 minutes.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Install cron daemon&lt;/span&gt;
&lt;span class="nb"&gt;sudo &lt;/span&gt;dnf &lt;span class="nb"&gt;install&lt;/span&gt; &lt;span class="nt"&gt;-y&lt;/span&gt; cronie
&lt;span class="nb"&gt;sudo &lt;/span&gt;systemctl &lt;span class="nb"&gt;enable&lt;/span&gt; &lt;span class="nt"&gt;--now&lt;/span&gt; crond

&lt;span class="c"&gt;# Add the job (as root)&lt;/span&gt;
&lt;span class="nb"&gt;sudo &lt;/span&gt;su -
&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"*/5 * * * * echo hello &amp;gt; /tmp/cron_text"&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&amp;gt;&lt;/span&gt; /var/spool/cron/root

&lt;span class="c"&gt;# Verify&lt;/span&gt;
crontab &lt;span class="nt"&gt;-l&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Cron syntax breakdown for &lt;code&gt;*/5 * * * *&lt;/code&gt;:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Position 1 (*/5): Every 5 minutes&lt;/li&gt;
&lt;li&gt;Position 2 (*): Every hour&lt;/li&gt;
&lt;li&gt;Position 3 (*): Every day of month&lt;/li&gt;
&lt;li&gt;Position 4 (*): Every month&lt;/li&gt;
&lt;li&gt;Position 5 (*): Every day of week&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Translation: "Run this every 5 minutes until the heat death of the universe."&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Day 7: Passwordless SSH - The Magic of Public Keys&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Set up passwordless SSH from jump host to all app servers. Because typing passwords repeatedly builds carpal tunnel, not character.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Generate SSH key pair&lt;/span&gt;
ssh-keygen &lt;span class="nt"&gt;-t&lt;/span&gt; rsa &lt;span class="nt"&gt;-b&lt;/span&gt; 4096

&lt;span class="c"&gt;# Copy to servers (the easy way)&lt;/span&gt;
ssh-copy-id tony@stapp01
ssh-copy-id steve@stapp02
ssh-copy-id banner@stapp03

&lt;span class="c"&gt;# Test it&lt;/span&gt;
ssh tony@stapp01 &lt;span class="nb"&gt;hostname&lt;/span&gt;  &lt;span class="c"&gt;# No password prompt = success&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The critical part: SSH is extremely picky about permissions.&lt;/p&gt;

&lt;p&gt;Required permissions:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;~/.ssh/                → 700 &lt;span class="o"&gt;(&lt;/span&gt;drwx------&lt;span class="o"&gt;)&lt;/span&gt;
~/.ssh/authorized_keys → 600 &lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nt"&gt;-rw-------&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
~/.ssh/id_rsa         → 600 &lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nt"&gt;-rw-------&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
~/.ssh/id_rsa.pub     → 644 &lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nt"&gt;-rw-r--r--&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Get these wrong and SSH will silently ignore your keys like you ignored the documentation.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Day 8: Ansible Installation - Version Hell&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Install Ansible. Sounds simple until they want a specific version available globally on all servers.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Wrong: User installation&lt;/span&gt;
python3 &lt;span class="nt"&gt;-m&lt;/span&gt; pip &lt;span class="nb"&gt;install&lt;/span&gt; &lt;span class="nt"&gt;--user&lt;/span&gt; ansible

&lt;span class="c"&gt;# Right: Global installation&lt;/span&gt;
&lt;span class="nb"&gt;sudo &lt;/span&gt;python3 &lt;span class="nt"&gt;-m&lt;/span&gt; pip &lt;span class="nb"&gt;install&lt;/span&gt; &lt;span class="s2"&gt;"ansible==4.8.0"&lt;/span&gt;

&lt;span class="c"&gt;# Verify&lt;/span&gt;
ansible &lt;span class="nt"&gt;--version&lt;/span&gt;
which ansible  &lt;span class="c"&gt;# Should show /usr/local/bin/ansible&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The difference:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;--user&lt;/code&gt; → Installs in ~/.local/bin (current user only)&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;sudo pip install&lt;/code&gt; → Installs in /usr/local/bin (all users)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Always use quotes around version specifiers: &lt;code&gt;"ansible==4.8.0"&lt;/code&gt; not &lt;code&gt;ansible==4.8.0&lt;/code&gt;. Shell expansion will mess you up.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Week 2 Stats&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Package managers tried: 2 (apt failed, dnf worked)&lt;/li&gt;
&lt;li&gt;Cron jobs created: 3 (one per server)&lt;/li&gt;
&lt;li&gt;SSH key pairs generated: 1 (4096-bit RSA)&lt;/li&gt;
&lt;li&gt;Ansible installations: 2 (user then global)&lt;/li&gt;
&lt;li&gt;"Not Ubuntu" realizations: 1 (the CentOS surprise)&lt;/li&gt;
&lt;li&gt;Times I typed &lt;code&gt;apt&lt;/code&gt; on CentOS: ~5 (muscle memory is stubborn)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Key Takeaways&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Commands I now know by heart:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;cat&lt;/span&gt; /etc/os-release          &lt;span class="c"&gt;# Always check your distro&lt;/span&gt;
crontab &lt;span class="nt"&gt;-l&lt;/span&gt;                   &lt;span class="c"&gt;# List cron jobs&lt;/span&gt;
ssh user@host &lt;span class="nb"&gt;hostname&lt;/span&gt;       &lt;span class="c"&gt;# Test passwordless SSH&lt;/span&gt;
ansible &lt;span class="nt"&gt;--version&lt;/span&gt;            &lt;span class="c"&gt;# Verify Ansible installation&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Mistakes that taught me:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Always check your OS before running commands&lt;/li&gt;
&lt;li&gt;SSH permissions must be exactly right&lt;/li&gt;
&lt;li&gt;Global vs user installations matter&lt;/li&gt;
&lt;li&gt;Version management is not optional&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;strong&gt;What's Next&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Days 9-12 coming up:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Ansible playbooks (finally using what I installed)&lt;/li&gt;
&lt;li&gt;Docker fundamentals&lt;/li&gt;
&lt;li&gt;Network configuration&lt;/li&gt;
&lt;li&gt;More troubleshooting (probably)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Full Article&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;This is a condensed version. For the complete writeup with more details, all the commands, and the full story of my UI struggles:&lt;/p&gt;

&lt;p&gt;👉 &lt;a href="https://dub.sh/kk5-8" rel="noopener noreferrer"&gt;Read the full article&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Following this challenge? Drop your "wrong distro" moment in the comments. The first time you tried Ubuntu commands on CentOS, or vice versa. We've all been there. 😅&lt;/p&gt;

</description>
      <category>devops</category>
      <category>ansible</category>
      <category>linux</category>
      <category>sysadmin</category>
    </item>
    <item>
      <title>Week 1 of KodeKloud’s 100 Days Challenge: Days 1-4 (Or: How I Learned to Stop Worrying and Love the Slow Labs</title>
      <dc:creator>Elijah</dc:creator>
      <pubDate>Fri, 19 Dec 2025 19:00:00 +0000</pubDate>
      <link>https://forem.com/elijahu/week-1-of-kodeklouds-100-days-challenge-days-1-4-or-how-i-learned-to-stop-worrying-and-love-the-25o6</link>
      <guid>https://forem.com/elijahu/week-1-of-kodeklouds-100-days-challenge-days-1-4-or-how-i-learned-to-stop-worrying-and-love-the-25o6</guid>
      <description>



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight markdown"&gt;&lt;code&gt;I decided to torture myself with KodeKloud's 100 Days of DevOps challenge. The VMs load slower than continental drift, but the hands-on Linux practice is actually solid.

Here's what I learned (and broke) in my first four days.

&lt;span class="gs"&gt;**Day 1: Non-Interactive Users**&lt;/span&gt;

The challenge was to create a user with a non-interactive shell.

&lt;span class="p"&gt;```&lt;/span&gt;&lt;span class="nl"&gt;
&lt;/span&gt;
bash
sudo useradd -s /usr/sbin/nologin username
getent passwd username


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;The mistake? I ran all these commands on the wrong host. Spent 10 minutes wondering why validation wasn't working before realizing I was on my local terminal instead of SSH'd into the lab.&lt;/p&gt;

&lt;p&gt;Lesson learned: Always double-check which host you're on. Run &lt;code&gt;hostname&lt;/code&gt; first.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Day 2: Temporary Users&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;The challenge was to create a user account with an expiration date.&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;
bash
sudo useradd anita -e 2023-06-05
chage -l anita


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;This creates a ticking time bomb account—perfect for temporary contractors or intern access.&lt;/p&gt;

&lt;p&gt;Bonus trick: Auto-lock account after 20 minutes:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;
bash
echo "usermod --lock username" | at now + 20 minutes


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;The mistake? Did it on the wrong host. Again. 🤦&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Day 3: Disabling Root SSH&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;The challenge was to disable root login across multiple servers.&lt;/p&gt;

&lt;p&gt;Manual approach:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;
bash
sudo vi /etc/ssh/sshd_config
# Change: #PermitRootLogin no → PermitRootLogin no
sudo systemctl restart sshd


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;Problem: I had to do this on three servers manually. Each SSH connection took 30 seconds—enough time to make coffee, question my life choices, and consider farming.&lt;/p&gt;

&lt;p&gt;Automation solution:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;
bash
for server in app1 app2 app3; do
    ssh $server "sudo sed -i 's/#PermitRootLogin no/PermitRootLogin no/' /etc/ssh/sshd_config &amp;amp;&amp;amp; sudo systemctl restart sshd"
done


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;My 30-minute nightmare became a 2-minute coffee break.&lt;/p&gt;

&lt;p&gt;Lesson learned: If you're doing something more than twice and it's boring, automate it.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Day 4: File Permissions&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;The challenge was to make a script executable for all users.&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;
bash
chmod 755 /tmp/xfusioncorp.sh
ls -l /tmp/xfusioncorp.sh


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;Output: &lt;code&gt;-rwxr-xr-x&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;Permission breakdown:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;7 (rwx) = full access for owner&lt;/li&gt;
&lt;li&gt;5 (r-x) = read and execute for group&lt;/li&gt;
&lt;li&gt;5 (r-x) = read and execute for others&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Common patterns worth memorizing:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;
bash
chmod 644 file.txt    # Owner: rw, Others: r
chmod 755 script.sh   # Owner: rwx, Others: rx
chmod 600 secret.txt  # Owner: rw, Others: nothing


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;&lt;strong&gt;Week 1 Stats&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Commands run: ~50&lt;/li&gt;
&lt;li&gt;Wrong host mistakes: 2&lt;/li&gt;
&lt;li&gt;Lab loading time: ~5 min/lab&lt;/li&gt;
&lt;li&gt;"Why isn't this working?" moments: 7&lt;/li&gt;
&lt;li&gt;Times I questioned my career: 3&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Key Takeaways&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Commands I now know by heart:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;
bash
useradd -s /usr/sbin/nologin username
chage -l username
chmod 755 script.sh


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Mistakes that taught me:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Always check &lt;code&gt;hostname&lt;/code&gt; before running commands&lt;/li&gt;
&lt;li&gt;Automate anything you do more than twice&lt;/li&gt;
&lt;li&gt;Lab speed ≠ learning speed&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;strong&gt;What's Next&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Days 5-8 are coming up:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Package management (yum, apt, dnf)&lt;/li&gt;
&lt;li&gt;Service management (systemctl deep dive)&lt;/li&gt;
&lt;li&gt;Cron jobs and scheduling&lt;/li&gt;
&lt;li&gt;More automation&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Full Article&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;This is a condensed version. For the complete writeup with more details, code examples, and commentary:&lt;/p&gt;

&lt;p&gt;👉 &lt;a href="https://dub.sh/kk1-4" rel="noopener noreferrer"&gt;Read the full article on my blog&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Following this challenge? Drop your biggest "wrong host" moment in the comments. I can't be the only one. 😅&lt;/p&gt;

</description>
      <category>devops</category>
      <category>linux</category>
      <category>kodekloud</category>
      <category>100daysofcode</category>
    </item>
  </channel>
</rss>
