<?xml version="1.0" encoding="UTF-8"?><rss xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:atom="http://www.w3.org/2005/Atom" version="2.0" xmlns:media="http://search.yahoo.com/mrss/"><channel><title><![CDATA[Jose's Tech Blog]]></title><description><![CDATA[Thoughts, troubleshooting and tutorials]]></description><link>https://cleverness.tech/</link><image><url>https://cleverness.tech/favicon.png</url><title>Jose&apos;s Tech Blog</title><link>https://cleverness.tech/</link></image><generator>Ghost 5.54</generator><lastBuildDate>Sun, 17 May 2026 08:39:55 GMT</lastBuildDate><atom:link href="https://cleverness.tech/rss/" rel="self" type="application/rss+xml"/><ttl>60</ttl><item><title><![CDATA[Migrating to docker-compose]]></title><description><![CDATA[<p>When I first setup my Docker VM on my Proxmox machine I did it on Ubuntu Server 18.04 LTS with a desktop environment installed because I was still very new to using command line. &#xA0;A desktop environment to manage everything with just seemed easier at the time. &#xA0;</p>]]></description><link>https://cleverness.tech/migrating-to-docker-compose/</link><guid isPermaLink="false">64752076d1c4ad0001fa4da0</guid><category><![CDATA[docker]]></category><category><![CDATA[proxmox]]></category><category><![CDATA[homelab]]></category><dc:creator><![CDATA[Jose Soto]]></dc:creator><pubDate>Mon, 29 May 2023 23:10:23 GMT</pubDate><media:content url="https://cleverness.tech/content/images/2023/05/docker4.png" medium="image"/><content:encoded><![CDATA[<img src="https://cleverness.tech/content/images/2023/05/docker4.png" alt="Migrating to docker-compose"><p>When I first setup my Docker VM on my Proxmox machine I did it on Ubuntu Server 18.04 LTS with a desktop environment installed because I was still very new to using command line. &#xA0;A desktop environment to manage everything with just seemed easier at the time. &#xA0;I also just installed everything with &apos;docker run&apos; commands and managed the images with Portainer. &#xA0;After completing a hardware overhaul and moving my Proxmox instance from 1 2U server to 3 Intel NUCs in a cluster I decided it was time to finally migrate all my various containers(minus NGINX) to 1 Docker VM. &#xA0;And since I&apos;m much more comfortable working off a command line now compared to 2018 I could do this in the regular Ubuntu Server environment.<br><br>The Docker VM was upgraded to 20.04 TLS at some point without much issue so first attempt was to create a backup of the image in Proxmox and update it to 22.04 LTS...which did not go as planned. &#xA0;Trying different methods either caused the install to fail or when it did complete I ran into an error many encountered in upgrading to 22.04 LTS that involves a conflict between network-manager and netplan. &#xA0;22.04 seems to default to netplan even if your install was using network-manager before the upgrade and while I WAS eventually able to address it(using DHCP in netplan but static addresses were still not working) I opted to just start on a fresh 22.04 image in a different one and migrate everything over for a fresh start.<br><br><strong>What are the benefits of docker-compose?</strong><br><br>While not necessary it makes deploying multiple images a lot easier and helps in managing apps that depend on others to run properly. &#xA0;Let&apos;s look at bookstack for example since it was initially one of the apps I was running. &#xA0;If I wanted to run it as a docker image according to the <a href="https://hub.docker.com/r/linuxserver/bookstack?ref=cleverness.tech">documentation on its Docker Hub page</a> it would look like this in the terminal</p><pre><code class="language-bash">docker run -d \
  --name=bookstack \
  -e PUID=1000 \
  -e PGID=1000 \
  -e TZ=Etc/UTC \
  -e APP_URL=&lt;yourbaseurl&gt; \
  -e DB_HOST=&lt;yourdbhost&gt; \
  -e DB_PORT=&lt;yourdbport&gt; \
  -e DB_USER=&lt;yourdbuser&gt; \
  -e DB_PASS=&lt;yourdbpass&gt; \
  -e DB_DATABASE=bookstackapp \
  -p 6875:80 \
  -v /path/to/data:/config \
  --restart unless-stopped \
  lscr.io/linuxserver/bookstack:latest</code></pre><p>This will pull the image for the first run and sets up access to a database, but this is a command that will be hard to reference later unless you copy it somewhere else or have easy access to the terminal history of this server. &#xA0;Also this assumes you have the database access already and doesn&apos;t automatically check to make sure the database is up before starting the image in the future to avoid errors. &#xA0;Let&apos;s compare this to the suggested docker-compose yaml file excerpt from the same documentation</p><pre><code class="language-bash">---
version: &quot;2&quot;
services:
  bookstack:
    image: lscr.io/linuxserver/bookstack
    container_name: bookstack
    environment:
      - PUID=1000
      - PGID=1000
      - APP_URL=https://bookstack.example.com
      - DB_HOST=bookstack_db
      - DB_PORT=3306
      - DB_USER=bookstack
      - DB_PASS=&lt;yourdbpass&gt;
      - DB_DATABASE=bookstackapp
    volumes:
      - ./bookstack_app_data:/config
    ports:
      - 6875:80
    restart: unless-stopped
    depends_on:
      - bookstack_db
  bookstack_db:
    image: lscr.io/linuxserver/mariadb
    container_name: bookstack_db
    environment:
      - PUID=1000
      - PGID=1000
      - MYSQL_ROOT_PASSWORD=&lt;yourdbpass&gt;
      - TZ=Europe/London
      - MYSQL_DATABASE=bookstackapp
      - MYSQL_USER=bookstack
      - MYSQL_PASSWORD=&lt;yourdbpass&gt;
    volumes:
      - ./bookstack_db_data:/config
    restart: unless-stopped</code></pre><p>In this proposed docker-compose file that contains nothing else we see some similarities to the docker run command they provide but notice 2 things immediately.</p><ol><li>This is easier to read in the yaml file compared to the docker run command. &#xA0;Since the docker-compose.yaml file will exist on the filesystem as long as you don&apos;t remove it, being able to see where a mistake was possibly made when deploying it is much easier to parse in this format or reference in the future if you want to add additional services to this docker-compose file.</li><li>The depends_on key in the file allows you to reference another container. &#xA0;If you don&apos;t have an external database and are going to be hosting 1 in a container specifically for bookstack you can include this command and it will make sure to wait for this container to start before starting the bookstack container. &#xA0;</li></ol><p>You can have multiple services in a docker-compose.yaml file too. &#xA0;Mine currently has this Ghost blog(and a mysql database it depends on), Gitea and Bookstack(plus a mariadb database it depends on). &#xA0;As long as there isn&apos;t any port conflicts it shouldn&apos;t cause any issues. &#xA0;After creating the docker-compose.yaml file running the following command in the directory pulled images and started everything</p><pre><code class="language-bash">docker-compose up</code></pre><p>If I want to have the containers running in the background to free up the terminal can use this instead(preferred for me)</p><pre><code class="language-bash">docker-compose up -d</code></pre><p>Its the same as the --detach option. &#xA0;If I want to bring all the images down I can run the following</p><pre><code class="language-bash">docker-compose down</code></pre><p>Do note that if you don&apos;t specify any volumes on the filesystem in the docker-compose file, doing this means you lose any data tied to that container. &#xA0;The bookstack + bookstack database specified above have mapped volumes so this is avoided. &#xA0;And updating images is as easy as either running</p><pre><code>docker-compose pull</code></pre><p>to update everything or if you wish to only update specific images(maybe you want to keep databases on specific versions) you can specify what image to pull by referencing the container name in the docker-compose.yaml file as seen below</p><pre><code class="language-bash">docker-compose pull bookstack</code></pre><p>This will only pull and update the bookstack service using the image reference in the compose file. &#xA0;You can find references to these commands and other docker-compose commands at the <a href="https://docs.docker.com/compose/reference/?ref=cleverness.tech">official docker documentation here</a>.<br><br>One final thing to note is that all these commands have to be run in the same directory the docker-compose.yaml file is located. &#xA0;One way I approached this is to just have my default directory when I log into this server be the location where the docker-compose file is located. &#xA0;You can achieve this by running the following command to append a line to your bashrc file</p><pre><code class="language-bash">echo &quot;cd /location/of/docker-compose&quot; &gt;&gt; ~/.bashrc</code></pre><p>This automatically puts me in the directory of the docker-compose.yaml file when I log in to save some time. &#xA0;And incase I ever want to use a different docker-compose file for testing in another directory I could edit this to change it to that directory, this way my permanent containers don&apos;t get effected by any commands I might be running for managing temporary container deployments.<br><br>Using docker-compose has allowed me to neatly consolidate 2 servers running desktop environments down to 1 barebones Ubuntu server for most of my permanent containers, using less resources and making it much easier to manage via CLI which I&apos;m more comfortable doing now than I was when I first started deploying these in 2018.</p>]]></content:encoded></item><item><title><![CDATA[Wireguard - fast, modern, secure VPN tunnel]]></title><description><![CDATA[<p>Wireguard is a VPN(virtual private network) protocol that allows you to connect to a network from a registered device outside of it. &#xA0;Once the connection is established, it&apos;s as if you are browsing your LAN. &#xA0;VPNs to home or business networks aren&apos;t anything</p>]]></description><link>https://cleverness.tech/wireguard-fast-modern-secure-vpn-tunnel/</link><guid isPermaLink="false">63fd7c753401ff000168da56</guid><category><![CDATA[linux]]></category><category><![CDATA[homelab]]></category><dc:creator><![CDATA[Jose Soto]]></dc:creator><pubDate>Wed, 23 Sep 2020 23:26:00 GMT</pubDate><media:content url="https://cleverness.tech/content/images/2023/02/wireguard2.png" medium="image"/><content:encoded><![CDATA[<img src="https://cleverness.tech/content/images/2023/02/wireguard2.png" alt="Wireguard - fast, modern, secure VPN tunnel"><p>Wireguard is a VPN(virtual private network) protocol that allows you to connect to a network from a registered device outside of it. &#xA0;Once the connection is established, it&apos;s as if you are browsing your LAN. &#xA0;VPNs to home or business networks aren&apos;t anything new, but the recent rise in popularity and usage in VPNs, as well as Wireguards directly into the Linux Kernel starting with version 5.6 has brought even more attention to it over the last few months. &#xA0;OpenVPN used to be the defacto VPN tunnel service used, but Wireguards performance blows it out of the water so far. &#xA0;<a href="https://www.vpnranks.com/blog/wireguard-vs-openvpn/?ref=cleverness.tech">Here&apos;s a chart from vpnranks.com that goes over their analysis of the throughput difference between Wireguard and OpenVPN</a>.<br></p><figure class="kg-card kg-image-card"><img src="https://cleverness.tech/content/images/2023/02/wireguard1.png" class="kg-image" alt="Wireguard - fast, modern, secure VPN tunnel" loading="lazy" width="700" height="420" srcset="https://cleverness.tech/content/images/size/w600/2023/02/wireguard1.png 600w, https://cleverness.tech/content/images/2023/02/wireguard1.png 700w"></figure><p>Picture taken from vpnranks.com, articled linked above<br>Wireguard can actually max out the gigabit connection without maxing out the CPU. &#xA0;And anyone who has tried using OpenVPN knows that it&apos;s actually very CPU intensive, many people choose their pfsense builds based on CPU throughput under OpenVPN. &#xA0;This is great for many reasons, as knowing that running a low power server on the cloud would not be bottlenecked by the CPU if you were using it as a VPN, meaning you can spin up a lower power server on the cloud for the same purpose and save alot of money over time.</p><p>So what&apos;s the best way to deploy Wireguard? &#xA0;It usually depends on your setup. &#xA0;If you are running a hypervisor like ESXI or Proxmox already, spinning up a VM dedicated to Wireguard is a viable option. &#xA0;Another option is to deploy it on a separate device such as a Raspberry Pi or another type of small maker board. &#xA0;Since as we&apos;ve established Wireguard is not a CPU intensive process, the small ARM chips on the Pi are more than enough to handle this.</p><p>I had a Pi 3B+ that I grabbed on sale from Microcenter awhile back so I went with that. &#xA0;It also supports USB booting out of the box which I like, so I formated a USB drive and after 2 restarts it booted right in. &#xA0;After changing the password(never leave the default password on a Raspberry Pi) and changing the username to obscure it abit from whatever roaming bots that try to communicate with it since I will be opening a port for Wireguard, it was ready to go.</p><p>PiMyLifeUp has a very simple guide that I used that they recently published, <a href="https://pimylifeup.com/raspberry-pi-wireguard/?ref=cleverness.tech">and i&apos;m gonna just link it here</a> since it does a great job of just getting Wireguard setup on a Pi very quickly. &#xA0;After setting it up and creating a profile to use on my Android phone, I grabbed the Android app and scanned the QR code to import the key. &#xA0;After fixing a port forward error on my router where I opened a TCP port instead of UDP, my connection was made!</p><p>Wireguard is not perfect though. &#xA0;Even it&apos;s creators have commented that it wasn&apos;t necessary designed with large commercial usage in mind. &#xA0;VPN providers like NordVPN, ExpressVPN, PureVPN, etc. &#xA0;There are concerns about privacy as by nature WireGuard does not dynamically assign IP addresses, so it logs and stores any IP address that connects to it. &#xA0;What this means that if you were to use the NordVPN implementation they have to take extra measures to ensure your connecting IP is obscured, as your IP being permanently logged is a pretty bad idea when using a VPN who has servers you have no access or control over. &#xA0;In this deployment we are using it less so to connect from our home network to the broad internet through a secure tunnel, but from the broad internet(in my case over a mobile network) to a home network through a secure tunnel. &#xA0;As long as there are no vulnerabilities with Wireguard itself, it&apos;s as secure as my home network.</p>]]></content:encoded></item><item><title><![CDATA[Linux UpSkill Challenge Day 10 through 20(End) - Cron,  SFTP, logrotate, and final thoughts on the lessons]]></title><description><![CDATA[<p>I ended up with an unexpected day off work yesterday, so I decided since I had some extra free time from coursework to finish up the rest of the days for the UpSkill challenge.</p><p>To finish up on the focus of logs that I&apos;ve been making sure I</p>]]></description><link>https://cleverness.tech/linux-upskill-challenge-day-10-through-20-end-cron-sftp-logrotate-and-final-thoughts-on-the-lessons/</link><guid isPermaLink="false">63fd7cba3401ff000168da65</guid><category><![CDATA[linux]]></category><dc:creator><![CDATA[Jose Soto]]></dc:creator><pubDate>Tue, 22 Sep 2020 12:00:00 GMT</pubDate><media:content url="https://cleverness.tech/content/images/2023/02/linuxday10.png" medium="image"/><content:encoded><![CDATA[<img src="https://cleverness.tech/content/images/2023/02/linuxday10.png" alt="Linux UpSkill Challenge Day 10 through 20(End) - Cron,  SFTP, logrotate, and final thoughts on the lessons"><p>I ended up with an unexpected day off work yesterday, so I decided since I had some extra free time from coursework to finish up the rest of the days for the UpSkill challenge.</p><p>To finish up on the focus of logs that I&apos;ve been making sure I get when completing this course, Day 18 covered log rotation. &#xA0;By going into /etc/logrotate.conf you can see what the default values are for rotating system logs. &#xA0;By default they are all global, but you also have the ability to specify specific rules for specific logs. &#xA0;Certain logs I might not care to check more than once a week, but things like auth.log or nginx/apache access and error logs I would want daily snapshots of. &#xA0;On this test server I didn&apos;t get that detailed, so the logrotate.conf for me looks like the image below.<br></p><figure class="kg-card kg-image-card"><img src="https://cleverness.tech/content/images/2023/02/linuxday10-1.png" class="kg-image" alt="Linux UpSkill Challenge Day 10 through 20(End) - Cron,  SFTP, logrotate, and final thoughts on the lessons" loading="lazy" width="638" height="444" srcset="https://cleverness.tech/content/images/size/w600/2023/02/linuxday10-1.png 600w, https://cleverness.tech/content/images/2023/02/linuxday10-1.png 638w"></figure><p>I changed the first uncommented line from weekly to daily, uncommented dateext and added the dateformat line below it. &#xA0;Note that you can format it however you wish, what I have up in dateformat is actually the default value if I left the rest of the link blank. &#xA0;Set it however you prefer to read them. &#xA0;You can see the last commented line on system-specific logs. &#xA0;Let&apos;s see I only wanted to hold 1 weeks worth of auth.log files to reduce clutter. &#xA0;I could add a specific configuration like this.</p><pre><code>/var/log/auth.log {
    rotate 7
    mail example@example.com
}
</code></pre><p>First line shows that after counting 7 log files, it will simply delete the oldest one instead of rotating it down further. &#xA0;This by itself will reduce clutter but it&apos;s also not exactly best practice, sometimes older logs can be important. &#xA0;Instead of simply deleting it, by adding in the mail line(if we setup mail service on this server) any old logs that were to be purged are instead mailed to that email address. &#xA0;This allows us to both reduce clutter in the directory but also maintain records of all these events on a seperate storage system. &#xA0;Even with everything moving to the cloud, having some data stored locally or at a different service for redundancy is very important.</p><p>Cronjobs were the other major takeaway from the last few lessons. &#xA0;Most good sysadmins are also lazy sysadmins, because they automate many tasks across many different servers and VMs. &#xA0;Cronjobs are how they get to do that. &#xA0;You write a script that then runs on a schedule. &#xA0;I wouldn&apos;t need this for the log management example I just talked about, but what if I just want a list of recently disconnected ssh attempts done to my server run everyday for me? &#xA0;I did a bash script last post about how to get that data and I put it in my path, a cronjob can be created that simply runs that for me once a day.</p><p>SFTP stands for SSH File Transfer Protocol, and it&apos;s a built in service for being able to send or receive files through SSH. &#xA0;So if I want to grab files off my server without using automation, sftp is the way to go. &#xA0;And if you setup your ssh connection in your config file already as I did, in my case connecting to my server is as easy as typing</p><pre><code>sftp linuxskillup
</code></pre><p>and you are in the shell, ready to copy whatever you want. &#xA0;<a href="https://linux.die.net/man/1/sftp?ref=cleverness.tech">The full manual page for sftp can be seen here</a>, but basic commands will be using get to receive from the remote host, or put to sent to the remote host. &#xA0;So if I want to copy over attacker.txt from this remote server to analyze it locally, I can simply type in the sftp shell</p><pre><code>get /home/cleverness/attackers.txt /home/cleverness/Downloads
</code></pre><p>first path is the remote servers path, second is my home devices path. &#xA0;If you are in the current directory of the file on the remote server, the full path isn&apos;t necessary, attackers.txt would have also worked since I was in the home directory there already. &#xA0;And in reverse, if I want to move the file back after I examined it and dated it, in the sftp shell I&apos;d go</p><pre><code>put /home/cleverness/Downloads/attackers-09222020.txt /home/cleverness
</code></pre><p>Home path followed by remote path. &#xA0;Nice way of securely transferring files between remote locations.</p><p>Overall I had a lot of fun with this challenge. &#xA0;Some days were more informative than others for me since I know some stuff from running linux servers in my home already but I still learned alot. &#xA0;Big thanks to snori74 on Github/Reddit for hosting the challenge. &#xA0;I have the pages I did for this last post on the bottom, <a href="https://github.com/snori74/linuxupskillchallenge?ref=cleverness.tech">but here&apos;s the full Github page again for anyone interested in starting fresh.</a></p><p><a href="https://github.com/snori74/linuxupskillchallenge/blob/master/10.md?ref=cleverness.tech">Day 10</a> <a href="https://github.com/snori74/linuxupskillchallenge/blob/master/11.md?ref=cleverness.tech">Day 11</a> <a href="https://github.com/snori74/linuxupskillchallenge/blob/master/12.md?ref=cleverness.tech">Day 12</a> <a href="https://github.com/snori74/linuxupskillchallenge/blob/master/13.md?ref=cleverness.tech">Day 13</a> <a href="https://github.com/snori74/linuxupskillchallenge/blob/master/14.md?ref=cleverness.tech">Day 14</a> <a href="https://github.com/snori74/linuxupskillchallenge/blob/master/15.md?ref=cleverness.tech">Day 15</a> <a href="https://github.com/snori74/linuxupskillchallenge/blob/master/16.md?ref=cleverness.tech">Day 16</a> <a href="https://github.com/snori74/linuxupskillchallenge/blob/master/17.md?ref=cleverness.tech">Day 17</a> <a href="https://github.com/snori74/linuxupskillchallenge/blob/master/18.md?ref=cleverness.tech">Day 18</a> <a href="https://github.com/snori74/linuxupskillchallenge/blob/master/19.md?ref=cleverness.tech">Day 19</a> <a href="https://github.com/snori74/linuxupskillchallenge/blob/master/20.md?ref=cleverness.tech">Day 20</a></p>]]></content:encoded></item><item><title><![CDATA[Linux UpSkill Challenge Day 7 through 9 - Apache, Grep, and Firewalls]]></title><description><![CDATA[<p>Day 7 involved installing Apache2 on my Ubuntu server, which in troubleshooting inadvertently ended up with me doing things in Day 9 until I figured out my error. &#xA0;Installing Apache allows you to display a webpage and route traffic on your server through Ports 80 and/or 443 depending</p>]]></description><link>https://cleverness.tech/linux-upskill-challenge-day-7-through-9-apache-grep-and-firewalls/</link><guid isPermaLink="false">63fd7b973401ff000168da3f</guid><category><![CDATA[linux]]></category><dc:creator><![CDATA[Jose Soto]]></dc:creator><pubDate>Wed, 16 Sep 2020 16:51:00 GMT</pubDate><media:content url="https://cleverness.tech/content/images/2023/02/linuxday7.png" medium="image"/><content:encoded><![CDATA[<img src="https://cleverness.tech/content/images/2023/02/linuxday7.png" alt="Linux UpSkill Challenge Day 7 through 9 - Apache, Grep, and Firewalls"><p>Day 7 involved installing Apache2 on my Ubuntu server, which in troubleshooting inadvertently ended up with me doing things in Day 9 until I figured out my error. &#xA0;Installing Apache allows you to display a webpage and route traffic on your server through Ports 80 and/or 443 depending on whether you have SSL setup. &#xA0;On Ubuntu 20.04 it&apos;s supposed to be a pretty simple install to get the sample page showing on your IP/Domain but I made an oversight as this was my host time hosting a cloud server.</p><p>My first step was to make sure ports were open to the internet, and really should have been my first clue as to the real solution. &#xA0;Checking the firewall with</p><pre><code>sudo ufw status
</code></pre><p>showed that the firewall was actually inactive by default on this installation. &#xA0;Normally it should be showing as active and what ports are in the list and whether they are allowed, as seen here</p><pre><code>cleverness@Linux-Skillup-Challenge-Ubuntu:~$ sudo ufw status
Status: active

To                         Action      From
--                         ------      ----
OpenSSH                    ALLOW       Anywhere                  
Apache                     ALLOW       Anywhere                  
OpenSSH (v6)               ALLOW       Anywhere (v6)             
Apache (v6)                ALLOW       Anywhere (v6) 
</code></pre><p>So in my infinite wisdom, I thought &quot;maybe if the firewall is on, connections will work through port 80&quot; even though I was connected remotely through SSH so that shouldn&apos;t be the case at all, and not how firewalls work, but I&apos;m not smart sometimes. &#xA0;So I went about finding out the best way to add everything to the firewall and turning it on without accidentally locking myself out, which is apparently an easy thing to do. &#xA0;Instead of adding in the ports manually, which is completely viable, I ended up using the &apos;app list&apos; command</p><pre><code>cleverness@Linux-Skillup-Challenge-Ubuntu:~$ sudo ufw app list
Available applications:
  Apache
  Apache Full
  Apache Secure
  OpenSSH
</code></pre><p>Here we see available applications that can be added to the firewall based on what is installed on this system. &#xA0;As it&apos;s very barebones there are literally only 2 listed. &#xA0;OpenSSH is just 1 port and pretty obvious since I am remote, so I add that first</p><pre><code>cleverness@Linux-Skillup-Challenge-Ubuntu:~$ sudo ufw allow OpenSSH
</code></pre><p>Then when it comes to Apache, it depends what I am going to be doing with it. &#xA0;If I want to use it as a reverse proxy I will probably want to do with Apache Full to allow connections from both port 80 and port 443, or Apache Secure if I want to only allow traffic through 443. &#xA0;As this is a temporary instance and most I&apos;ll be doing before it disappears is trying out some web templates I just selected Apache.</p><pre><code>cleverness@Linux-Skillup-Challenge-Ubuntu:~$ sudo ufw allow Apache
</code></pre><p>Then I enabled the Firewall and hope I didnt mess anything up, because if I did I was about to get disconnected from my SSH session.</p><pre><code>cleverness@Linux-Skillup-Challenge-Ubuntu:~$ sudo ufw enable
</code></pre><p>I wasn&apos;t disconnected so it worked, and the firewall status shows active as I displayed above earlier. &#xA0;But the Apache default page still wasn&apos;t loading. &#xA0;After some Google-fu and asking on Discord, it dawned on me that I never allowed traffic through port 80 on Azure itself. &#xA0;Similar to port forwarding on your home router, if you don&apos;t open the ports for the VM on Azure they won&apos;t allow traffic through it, for security reasons. &#xA0;Port 22 was created by default when I first spun the VM up as I requested it but it slipped my mind right after doing so that I might need to do this again.<br></p><figure class="kg-card kg-image-card kg-card-hascaption"><img src="https://cleverness.tech/content/images/2023/02/linuxday9.png" class="kg-image" alt="Linux UpSkill Challenge Day 7 through 9 - Apache, Grep, and Firewalls" loading="lazy" width="1599" height="224" srcset="https://cleverness.tech/content/images/size/w600/2023/02/linuxday9.png 600w, https://cleverness.tech/content/images/size/w1000/2023/02/linuxday9.png 1000w, https://cleverness.tech/content/images/2023/02/linuxday9.png 1599w" sizes="(min-width: 720px) 720px"><figcaption>Networking Settings for the VM in Azure where the port has to be opened</figcaption></figure><p>Literally right after I added the inbound rule, the default page worked! &#xA0;So that solved that dilemma for me.</p><p>Day 8 dealt with using different commands for log reading and management. &#xA0;One of my 2 big takeaways was using tail -f /path/to/log allows for real-time log reading. &#xA0;So for example if on my NGINX VM I want to see real-time logs for this website, I would go</p><pre><code>tail -f /var/log/nginx/cleverness.tech.access.log
</code></pre><p>the terminal window will then continue running, showing me when the pages are being accessed. &#xA0;As this site doesn&apos;t get a ton of traffic it&apos;s very easy to follow. &#xA0;But what if you are using a remote or local environment and many attempts per minute are being made to access your server? &#xA0;It&apos;s still possible to do this but being able to break it down and output it somewhere is very helpful as a System Administrator. &#xA0;That&apos;s where grep, the second big takeaway from Day 8 came in.</p><p>grep allows you to search and filter words and phrases from files, but it can also be used to further narrow down a previous grep search. &#xA0;So if after examining logs I see many failed attempts at logging in with different usernames, I can narrow it down with something like</p><pre><code>grep &quot;authenticating&quot; /var/log/auth.log| grep -v &quot;root&quot;
</code></pre><p>It&apos;ll first filter the lines in auth.log with authenticating in them, and then further filter them down to ones that include root. &#xA0;The Day 8 course that goes into how to format the output using the cut command, and outputting that output to a text file for future reading and analysis.</p><pre><code>grep &quot;authenticating&quot; /var/log/auth.log| grep -v &quot;root&quot;| cut -f 10- -d&quot; &quot; &gt; attackers.txt
</code></pre><p>Depending on the log you are viewing and the line layout you may have to adjust the values abit, but it will leave you with an output of only IP addresses which you can use for future analysis. &#xA0;And since auth.log logs many different connection types with different message, you can just change the grep inputs and once you get the output you desire, append it to the end attackers.txt with &gt;&gt; instead of &gt; for a large file.</p><p>Instead of cut there is another program called awk which is more difficult to use but gives you more control over where you are pulling out from a file if you know where it is. &#xA0;I found a nice example <a href="https://gist.github.com/gfoss/4728912?ref=cleverness.tech">off this Github page</a></p><pre><code>grep -rhi &apos;invalid&apos; /var/log/auth.log* | awk &apos;{print $10}&apos; | uniq | sort &gt; ~/ips.txt
</code></pre><p>This will grab the IPs from the 10th field after only grabbing the lines containing invalid, only list uniq values to avoid repeats, and sort them and pipe the output out to ips.txt &#xA0;I tried looking into using an alias for grep and alias are not really super friendly when it comes to accepting multiple inputs like I would think about using for this situation, so I ended up learning a bit abunch bash functions.</p><p>Bash functions are custom functions you can write that can be executed any time, and in my case I can also pass through some values into them if I write them that way. &#xA0;First I need to open the file I need to edit for all this to work like so</p><pre><code>nano ~/.bashrc
</code></pre><p>At the very bottom of the bash file is where I am going to add in my function.</p><pre><code>authscan() {
        grep -rhi &quot;$1&quot; /var/log/auth.log* | awk &apos;{print $10}&apos; | uniq | sort 
}
</code></pre><p>If you look at the line for authscan() you can see it&apos;s very similar to the last grep line with a small difference; The &quot;$1&quot; is where I want to pass through a specific value when running the function. &#xA0;As I am only going to pass in 1 grep argument I am going to only pass in 1, but if I wanted to scan for different logs I could add a &quot;$2&quot; where the log file path is defined. &#xA0;However if doing so, you might need to append awk manually when calling the function as it&apos;s very precise where it prints from. &#xA0;Like my Apache and NGINX logs start with the IP address right at the beginning of the line in the logs, while auth.log will have it somewhere else depending on the type of connection occurring.</p><p>After saving and exiting, run the following to reload bash without disconnecting you from your environment</p><pre><code>exec bash
</code></pre><p>Now to test it, I simply run the following to make sure I get the same output as the full grep command from earlier.</p><pre><code>authscan &apos;invalid&apos; &gt; ips2.txt
</code></pre><p>And I end up with the same text file. &#xA0;So if I don&apos;t feel like hitting the up arrow 10-40 times to look for the grep command I can use bash functions on my actual servers to get the same result. &#xA0;The grep code I found isn&apos;t 100% perfect in filtering out everything that isn&apos;t an IP due to how wildly different the sentence structure is in auth.log, but in the apache2 access log it actually works flawlessly since the first field on each line in those logs is the connecting IP. &#xA0;So for my NGINX VM by swiping the 10 for 1 in the print portion I get a much cleaner list of IPs in my output.</p><p>Github <a href="https://github.com/snori74/linuxupskillchallenge/blob/master/07.md?ref=cleverness.tech">Day 7</a> <a href="https://github.com/snori74/linuxupskillchallenge/blob/master/08.md?ref=cleverness.tech">Day 8</a> <a href="https://github.com/snori74/linuxupskillchallenge/blob/master/09.md?ref=cleverness.tech">Day 9</a></p>]]></content:encoded></item><item><title><![CDATA[Linux UpSkill Challenge Day 4 through 6 - Installing applications, tab complete and vim + vimtutor]]></title><description><![CDATA[<p>Days 4 through 6 covers how to actually install applications remotely, some quality of life commands to make using the command line better, and vim. &#xA0;Since these are things I have some familiarity with I figured it&apos;d be best to go over vimtutor here as opposed to</p>]]></description><link>https://cleverness.tech/linux-upskill-challenge-day-4-through-6-installing-applications-tab-complete-and-vim-vimtutor/</link><guid isPermaLink="false">63fd7ae63401ff000168da23</guid><category><![CDATA[linux]]></category><dc:creator><![CDATA[Jose Soto]]></dc:creator><pubDate>Sun, 13 Sep 2020 19:24:00 GMT</pubDate><media:content url="https://cleverness.tech/content/images/2023/02/linuxday4.png" medium="image"/><content:encoded><![CDATA[<img src="https://cleverness.tech/content/images/2023/02/linuxday4.png" alt="Linux UpSkill Challenge Day 4 through 6 - Installing applications, tab complete and vim + vimtutor"><p>Days 4 through 6 covers how to actually install applications remotely, some quality of life commands to make using the command line better, and vim. &#xA0;Since these are things I have some familiarity with I figured it&apos;d be best to go over vimtutor here as opposed to in a 100DaysofCode post when I start that up again as it is relevant.</p><p>Day 4 goes through how to use the apt command in Ubuntu to install applications from the official repositories. &#xA0;Whichever linux distribution you install will have a list of repositories installed by default. &#xA0;<a href="https://packages.ubuntu.com/?ref=cleverness.tech">You can browse the official Ubuntu repository here</a>, either by selecting the release you are on and manually searching if you know what you want already. &#xA0;Very useful if you need something specific like a python debugger and want to see what applications Canonical allows in their repositories. &#xA0;There&apos;s also the option of adding in extra repositories for access to other programs. &#xA0;When using Fedora this was actually encouraged, as the RPM Fusion community repository contains a large amount of third-party applications that aren&apos;t in the main Fedora one.</p><p>Day 5 goes through some quality of life techniques to using the command line, the biggest one for me is tab to autocomplete a word you start typing. &#xA0;Very quick way to get lines written or to autocomplete a ridiculously long Docker Container ID if you need to get into the folder for something.</p><p>Day 6 covers using vim as an editor, and it&apos;s something I covered abit before when first starting the posts about Python. &#xA0;To bring something new on the subject, I am gonna bring up what I&apos;ve been doing recently to brush up on vim which is a program called vimtutor. &#xA0;It&apos;s a program that to my knowledge comes already included into the distro if it has vim(which most do nowadays by default) and it&apos;s essentially a large text document with instructions on how to navigate and use the various vim commands.<br></p><figure class="kg-card kg-image-card kg-card-hascaption"><img src="https://cleverness.tech/content/images/2023/02/linuxday6.png" class="kg-image" alt="Linux UpSkill Challenge Day 4 through 6 - Installing applications, tab complete and vim + vimtutor" loading="lazy" width="840" height="965" srcset="https://cleverness.tech/content/images/size/w600/2023/02/linuxday6.png 600w, https://cleverness.tech/content/images/2023/02/linuxday6.png 840w" sizes="(min-width: 720px) 720px"><figcaption>Screenshot of the beginning of the vimtutor page</figcaption></figure><p>Each lesson as you can see above teaches you how to do things in vim, as it&apos;s not your standard editor with it&apos;s reliance on the different modes + keyboard shortcuts. &#xA0;Lesson 1.2 early on even teaches what is probably the most googled thing about vi/vim which is &quot;how do you exit vim?&quot; so people can finally leave it! &#xA0;Best thing is this is repeatable, so working through this once a day has helped me greatly in getting vim down more than just trying to write beginner Python programs in it has. &#xA0;Definitely look into if you are thinking about starting to use vim.</p><p>Github repo pages for these days challenges; <a href="https://github.com/snori74/linuxupskillchallenge/blob/master/04.md?ref=cleverness.tech">Day 4</a> <a href="https://github.com/snori74/linuxupskillchallenge/blob/master/05.md?ref=cleverness.tech">Day 5</a> <a href="https://github.com/snori74/linuxupskillchallenge/blob/master/06.md?ref=cleverness.tech">Day 6</a></p>]]></content:encoded></item><item><title><![CDATA[Linux UpSkill Challenge Day 3 - sudo and auth.log]]></title><description><![CDATA[<p>One thing that I&apos;ve been pretty poor at is checking logs. &#xA0;I think I can count on 1 hand the number of time I&apos;ve used Event Viewer on Windows in the 21 years I was using it. &#xA0;It wasn&apos;t until I started</p>]]></description><link>https://cleverness.tech/linux-upskill-challenge-day-3-sudo-and-auth-log/</link><guid isPermaLink="false">63fd79df3401ff000168da02</guid><category><![CDATA[linux]]></category><dc:creator><![CDATA[Jose Soto]]></dc:creator><pubDate>Wed, 09 Sep 2020 13:32:00 GMT</pubDate><media:content url="https://cleverness.tech/content/images/2023/02/linuxday3.png" medium="image"/><content:encoded><![CDATA[<img src="https://cleverness.tech/content/images/2023/02/linuxday3.png" alt="Linux UpSkill Challenge Day 3 - sudo and auth.log"><p>One thing that I&apos;ve been pretty poor at is checking logs. &#xA0;I think I can count on 1 hand the number of time I&apos;ve used Event Viewer on Windows in the 21 years I was using it. &#xA0;It wasn&apos;t until I started using NGINX as my reverse proxy that I started actively looking at them. &#xA0;Browsing the access.log and error.log files gave me a glimpse as to who was accessing my domain and specifically what. &#xA0;I learned something similar doing the day 3 challenge when it came to /var/log/auth.log.</p><p><a href="https://github.com/snori74/linuxupskillchallenge/blob/master/03.md?ref=cleverness.tech">Day 3 Github Page</a></p><p>Since this is a remote server any login attempt is logged through there, and since I secured the server with keys instead of a password majority of the attempts by the bots don&apos;t make it past that part. &#xA0;There are a few that seem to have a set of keys to use as pictured below, what exactly they are looking for I do not know but I imagine that&apos;s the type of thing a honeypot would be good for.</p><figure class="kg-card kg-image-card kg-card-hascaption"><img src="https://cleverness.tech/content/images/2023/02/linuxday3-1-1.png" class="kg-image" alt="Linux UpSkill Challenge Day 3 - sudo and auth.log" loading="lazy" width="1920" height="1080" srcset="https://cleverness.tech/content/images/size/w600/2023/02/linuxday3-1-1.png 600w, https://cleverness.tech/content/images/size/w1000/2023/02/linuxday3-1-1.png 1000w, https://cleverness.tech/content/images/size/w1600/2023/02/linuxday3-1-1.png 1600w, https://cleverness.tech/content/images/2023/02/linuxday3-1-1.png 1920w" sizes="(min-width: 720px) 720px"><figcaption>whole lot of failed remote ssh login with keys</figcaption></figure><p>I see a whole bunch of Diffie-Hellman key information in the logs and wasn&apos;t sure what they were exactly. &#xA0;Apparently DH keys are one of the earliest examples of public key protocols and still used by many today, although RSA keys which I am more familiar with came after and are more commonly used as public keys nowadays. &#xA0;There was a paper also published in October 2015 <a href="https://www.microsoft.com/en-us/research/publication/imperfect-forward-secrecy-how-diffie-hellman-fails-in-practice/?ref=cleverness.tech">which I will link to here</a>(don&apos;t want to directly link to the PDF download for security reasons, but clicking view publication will download it for those to wish to read it in full) that shows that DH keys are not as secure as believed. &#xA0;Considering how many unupgraded systems there are on the net it wouldn&apos;t surprise me if there are some exposed systems still using this type of keys that the bots are looking to compromise with some keys they found on the darkweb.<br><br>Log management is an important role in being a system administrator so just this portion of Day 3 alone was good for me to learn and keep in the back of my mind.</p>]]></content:encoded></item><item><title><![CDATA[Linux UpSkill Challenge Day 0 through 2 - Setup and CLI Basics]]></title><description><![CDATA[<p>I took a big break during the summer with work starting up again and everything happening with COVID-19 here in the city, I will get back to the Python practice as much as I can during this semester that sees me taking 5 classes while distance learning.</p><pre><code>One extra bit</code></pre>]]></description><link>https://cleverness.tech/linux-upskill-challenge-day-0-through-2-setup-and-cli-basics/</link><guid isPermaLink="false">63fc3c3b3940fd00015bb767</guid><category><![CDATA[linux]]></category><dc:creator><![CDATA[Jose Soto]]></dc:creator><pubDate>Tue, 08 Sep 2020 13:34:00 GMT</pubDate><media:content url="https://cleverness.tech/content/images/2023/02/linuxday0-2.png" medium="image"/><content:encoded><![CDATA[<img src="https://cleverness.tech/content/images/2023/02/linuxday0-2.png" alt="Linux UpSkill Challenge Day 0 through 2 - Setup and CLI Basics"><p>I took a big break during the summer with work starting up again and everything happening with COVID-19 here in the city, I will get back to the Python practice as much as I can during this semester that sees me taking 5 classes while distance learning.</p><pre><code>One extra bit of training that I came across while browsing /r/linux was this thing called the [Linux UpSkill Challenge](https://linuxupskillchallenge.com/). &#xA0;Lovely chap who goes by snori74 on Reddit and Github has a 20 day course setup to help people learn basic remote sysadmin commands. &#xA0;Importance of people learning how to remotely control and move around their VMs is pretty important with so many services being migrated to the Cloud as opposed to being run locally, and with remote work possibly being more accepted due to this pandemic it&apos;s probably even more important.

I know some of these basic commands from working with my Unraid and Proxmox server, plus my normal navigation in Pop!_OS terminal but it doesn&apos;t hurt to have it reinforced through practice like this. &#xA0;And I had $100 in Azure credits through using my student email so it cost me nothing upfront to setup a VPS through them for this. &#xA0;

Day 0 was to be done anytime before September 7th, 2020 which was simply setting up your VM on whatever service you chose, whether it be DigitalOcean/AWS/Azure/etc. &#xA0;Creating the VM in Azure gave me the option to download the Private Key, as I opted for enabling SSH through the use of keys as opposed to a password since this was going to be open to the internet. &#xA0;Now, I never used SSH before on my machine. &#xA0;So without going too much into specifics of how I setup my SSH I followed this nice [post on Linuxize.com](https://linuxize.com/post/using-the-ssh-config-file/) about how to do it and also how to edit your ssh config file. &#xA0;As opposed to needing to specific the public IP, port and key needed everytime you can create an alias so instead of needing to type all that, to access my VPS(virtual private server) for this course I simply type

ssh linuxskillup
</code></pre><p>in the terminal and I am connected to my VPS. &#xA0;Day 1 is to ensure we can connect to the server and then run some simple commands as shown below. &#xA0;The <a href="https://github.com/snori74/linuxupskillchallenge/blob/master/01.md?ref=cleverness.tech">day 1 task list on snori74&apos;s Github can be found here</a>.</p><pre><code>cleverness@Linux-Skillup-Challenge-Ubuntu:~$ ls
cleverness@Linux-Skillup-Challenge-Ubuntu:~$ uptime
  14:56:56 up 3 days, 22:23,  1 user,  load average: 0.00, 0.00, 0.00
cleverness@Linux-Skillup-Challenge-Ubuntu:~$ free
              total        used        free      shared  buff/cache   available
Mem:         938456      188864      170352         908      579240      591652
Swap:             0           0           0
cleverness@Linux-Skillup-Challenge-Ubuntu:~$ df -h
Filesystem      Size  Used Avail Use% Mounted on
/dev/root        29G  1.8G   28G   6% /
devtmpfs        455M     0  455M   0% /dev
tmpfs           459M     0  459M   0% /dev/shm
tmpfs            92M  888K   91M   1% /run
tmpfs           5.0M     0  5.0M   0% /run/lock
tmpfs           459M     0  459M   0% /sys/fs/cgroup
/dev/sda15      105M  9.1M   96M   9% /boot/efi
/dev/loop0       56M   56M     0 100% /snap/core18/1885
/dev/loop1       72M   72M     0 100% /snap/lxd/16740
/dev/loop2       30M   30M     0 100% /snap/snapd/8790
/dev/sdb1       3.9G   16M  3.7G   1% /mnt
/dev/loop3       71M   71M     0 100% /snap/lxd/16922
tmpfs            92M     0   92M   0% /run/user/1000
cleverness@Linux-Skillup-Challenge-Ubuntu:~$ uname -a
Linux Linux-Skillup-Challenge-Ubuntu 5.4.0-1023-azure #23-Ubuntu SMP Mon Aug 17 20:33:19 UTC 2020 x86_64 x86_64 x86_64 GNU/Linux
</code></pre><p>Day 2 was to get used to the concept of how to move across the linux filesystem through the terminal. &#xA0;Things i&apos;ve done before but still a good refresher. &#xA0;In the terminal output below I kept the mistake I made when creating the test directory and not putting the period infront initially to show what happens when you mess up. &#xA0;Instead of creating the directory locally in my home directory, I accidentally created it in /test under the root directory. &#xA0;That&apos;s why the first ls output after creating the directory was empty. &#xA0;Removing it and creating it correctly allowed for the output to now be seen as expected. &#xA0;<a href="https://github.com/snori74/linuxupskillchallenge/commit/7f59e31a312042ee646a8d44f561ee5d0c531974?ref=cleverness.tech">Day 2 task list can be found here</a>. &#xA0;Since I am saving time from commuting due to distance learning I am looking to make sure I work on things like this and the Python 100 days of coding more accurately now that I have a sense of how to manage my time between work and school during this unique semester.</p><pre><code>cleverness@Linux-Skillup-Challenge-Ubuntu:~$ pwd
/home/cleverness
cleverness@Linux-Skillup-Challenge-Ubuntu:~$ cd /var/log
cleverness@Linux-Skillup-Challenge-Ubuntu:/var/log$ pwd
/var/log
cleverness@Linux-Skillup-Challenge-Ubuntu:/var/log$ cd ..
cleverness@Linux-Skillup-Challenge-Ubuntu:/var$ pwd  
/var
cleverness@Linux-Skillup-Challenge-Ubuntu:/var$ cd ..
cleverness@Linux-Skillup-Challenge-Ubuntu:/$ pwd
/
cleverness@Linux-Skillup-Challenge-Ubuntu:/$ cd /var
cleverness@Linux-Skillup-Challenge-Ubuntu:/var$ cd /log
-bash: cd: /log: No such file or directory
cleverness@Linux-Skillup-Challenge-Ubuntu:/var$ cd ./log 
cleverness@Linux-Skillup-Challenge-Ubuntu:/var/log$ pwd
/var/log
cleverness@Linux-Skillup-Challenge-Ubuntu:/var/log$ ls
alternatives.log  auth.log.1  chrony                 dist-upgrade  dmesg.1.gz  kern.log    lastlog  syslog.1     syslog.4.gz          wtmp
apt               azure       cloud-init-output.log  dmesg         dpkg.log    kern.log.1  private  syslog.2.gz  unattended-upgrades
auth.log          btmp        cloud-init.log         dmesg.0       journal     landscape   syslog   syslog.3.gz  waagent.log
cleverness@Linux-Skillup-Challenge-Ubuntu:/var/log$ ls -l -t
total 1492
-rw-rw-r--  1 root      utmp            292292 Sep  8 08:07 lastlog
-rw-rw-r--  1 root      utmp             12288 Sep  8 08:07 wtmp
-rw-r-----  1 syslog    adm              70469 Sep  8 08:07 syslog
-rw-r-----  1 syslog    adm             252184 Sep  8 08:07 auth.log
-rw-r--r--  1 root      root             87858 Sep  8 07:51 waagent.log
-rw-r--r--  1 root      root             75130 Sep  8 06:11 dpkg.log
drwxr-xr-x  2 root      root              4096 Sep  8 06:11 apt
-rw-r-----  1 syslog    adm               1968 Sep  8 06:11 kern.log
-rw-rw----  1 root      utmp             85632 Sep  8 04:38 btmp
-rw-r-----  1 syslog    adm              36618 Sep  8 00:00 syslog.1
-rw-r-----  1 syslog    adm               4977 Sep  7 00:00 syslog.2.gz
-rw-r-----  1 syslog    adm               3898 Sep  6 00:00 syslog.3.gz
-rw-r-----  1 syslog    adm             256456 Sep  5 23:49 auth.log.1
-rw-r-----  1 syslog    adm              67937 Sep  5 00:00 syslog.4.gz
drwxr-x---  2 root      adm               4096 Sep  4 02:01 unattended-upgrades
-rw-r-----  1 syslog    adm             112130 Sep  3 20:28 kern.log.1
-rw-r--r--  1 root      root              7839 Sep  3 16:33 cloud-init-output.log
-rw-r--r--  1 syslog    adm             260953 Sep  3 16:33 cloud-init.log
-rw-r--r--  1 root      adm              34207 Sep  3 16:33 dmesg
-rw-r--r--  1 root      root              1906 Sep  3 15:23 alternatives.log
-rw-r--r--  1 root      adm              38769 Sep  3 15:22 dmesg.0
drwxr-xr-x  2 landscape landscape         4096 Sep  3 15:20 landscape
-rw-r--r--  1 root      adm              11571 Sep  3 15:18 dmesg.1.gz
drwxr-xr-x  2 root      root              4096 Sep  3 15:18 azure
drwx------  2 root      root              4096 Sep  3 15:18 private
drwxr-sr-x+ 3 root      systemd-journal   4096 Sep  3 15:18 journal
drwxr-xr-x  2 root      root              4096 Aug  3 12:26 dist-upgrade
drwxr-xr-x  2 _chrony   _chrony           4096 May 20 04:16 chrony
cleverness@Linux-Skillup-Challenge-Ubuntu:/var/log$ ls -l
total 1492
-rw-r--r--  1 root      root              1906 Sep  3 15:23 alternatives.log
drwxr-xr-x  2 root      root              4096 Sep  8 06:11 apt
-rw-r-----  1 syslog    adm             252184 Sep  8 08:07 auth.log
-rw-r-----  1 syslog    adm             256456 Sep  5 23:49 auth.log.1
drwxr-xr-x  2 root      root              4096 Sep  3 15:18 azure
-rw-rw----  1 root      utmp             85632 Sep  8 04:38 btmp
drwxr-xr-x  2 _chrony   _chrony           4096 May 20 04:16 chrony
-rw-r--r--  1 root      root              7839 Sep  3 16:33 cloud-init-output.log
-rw-r--r--  1 syslog    adm             260953 Sep  3 16:33 cloud-init.log
drwxr-xr-x  2 root      root              4096 Aug  3 12:26 dist-upgrade
-rw-r--r--  1 root      adm              34207 Sep  3 16:33 dmesg
-rw-r--r--  1 root      adm              38769 Sep  3 15:22 dmesg.0
-rw-r--r--  1 root      adm              11571 Sep  3 15:18 dmesg.1.gz
-rw-r--r--  1 root      root             75130 Sep  8 06:11 dpkg.log
drwxr-sr-x+ 3 root      systemd-journal   4096 Sep  3 15:18 journal
-rw-r-----  1 syslog    adm               1968 Sep  8 06:11 kern.log
-rw-r-----  1 syslog    adm             112130 Sep  3 20:28 kern.log.1
drwxr-xr-x  2 landscape landscape         4096 Sep  3 15:20 landscape
-rw-rw-r--  1 root      utmp            292292 Sep  8 08:07 lastlog
drwx------  2 root      root              4096 Sep  3 15:18 private
-rw-r-----  1 syslog    adm              70469 Sep  8 08:07 syslog
-rw-r-----  1 syslog    adm              36618 Sep  8 00:00 syslog.1
-rw-r-----  1 syslog    adm               4977 Sep  7 00:00 syslog.2.gz
-rw-r-----  1 syslog    adm               3898 Sep  6 00:00 syslog.3.gz
-rw-r-----  1 syslog    adm              67937 Sep  5 00:00 syslog.4.gz
drwxr-x---  2 root      adm               4096 Sep  4 02:01 unattended-upgrades
-rw-r--r--  1 root      root             87858 Sep  8 07:51 waagent.log
-rw-rw-r--  1 root      utmp             12288 Sep  8 08:07 wtmp
cleverness@Linux-Skillup-Challenge-Ubuntu:/var/log$ ls -l -t -r -a
total 1500
drwxr-xr-x   2 _chrony   _chrony           4096 May 20 04:16 chrony
drwxr-xr-x   2 root      root              4096 Aug  3 12:26 dist-upgrade
drwxr-xr-x  13 root      root              4096 Aug 14 17:41 ..
drwxr-sr-x+  3 root      systemd-journal   4096 Sep  3 15:18 journal
drwx------   2 root      root              4096 Sep  3 15:18 private
drwxr-xr-x   2 root      root              4096 Sep  3 15:18 azure
-rw-r--r--   1 root      adm              11571 Sep  3 15:18 dmesg.1.gz
drwxr-xr-x   2 landscape landscape         4096 Sep  3 15:20 landscape
-rw-r--r--   1 root      adm              38769 Sep  3 15:22 dmesg.0
-rw-r--r--   1 root      root              1906 Sep  3 15:23 alternatives.log
-rw-r--r--   1 root      adm              34207 Sep  3 16:33 dmesg
-rw-r--r--   1 syslog    adm             260953 Sep  3 16:33 cloud-init.log
-rw-r--r--   1 root      root              7839 Sep  3 16:33 cloud-init-output.log
-rw-r-----   1 syslog    adm             112130 Sep  3 20:28 kern.log.1
drwxr-x---   2 root      adm               4096 Sep  4 02:01 unattended-upgrades
-rw-r-----   1 syslog    adm              67937 Sep  5 00:00 syslog.4.gz
-rw-r-----   1 syslog    adm             256456 Sep  5 23:49 auth.log.1
-rw-r-----   1 syslog    adm               3898 Sep  6 00:00 syslog.3.gz
-rw-r-----   1 syslog    adm               4977 Sep  7 00:00 syslog.2.gz
-rw-r-----   1 syslog    adm              36618 Sep  8 00:00 syslog.1
drwxrwxr-x  10 root      syslog            4096 Sep  8 00:00 .
-rw-rw----   1 root      utmp             85632 Sep  8 04:38 btmp
-rw-r-----   1 syslog    adm               1968 Sep  8 06:11 kern.log
drwxr-xr-x   2 root      root              4096 Sep  8 06:11 apt
-rw-r--r--   1 root      root             75130 Sep  8 06:11 dpkg.log
-rw-r--r--   1 root      root             87858 Sep  8 07:51 waagent.log
-rw-r-----   1 syslog    adm             252184 Sep  8 08:07 auth.log
-rw-r-----   1 syslog    adm              70469 Sep  8 08:07 syslog
-rw-rw-r--   1 root      utmp             12288 Sep  8 08:07 wtmp
-rw-rw-r--   1 root      utmp            292292 Sep  8 08:07 lastlog
cleverness@Linux-Skillup-Challenge-Ubuntu:/var/log$ cd
cleverness@Linux-Skillup-Challenge-Ubuntu:~$ ls -ltra
total 36
-rw-r--r-- 1 cleverness cleverness  807 Feb 25  2020 .profile
-rw-r--r-- 1 cleverness cleverness 3771 Feb 25  2020 .bashrc
-rw-r--r-- 1 cleverness cleverness  220 Feb 25  2020 .bash_logout
drwxr-xr-x 3 root       root       4096 Sep  3 15:18 ..
drwx------ 2 cleverness cleverness 4096 Sep  3 15:18 .ssh
drwx------ 2 cleverness cleverness 4096 Sep  3 15:20 .cache
-rw-r--r-- 1 cleverness cleverness    0 Sep  3 15:21 .sudo_as_admin_successful
drwx------ 3 cleverness cleverness 4096 Sep  3 16:34 .config
drwxr-xr-x 5 cleverness cleverness 4096 Sep  3 16:34 .
-rw------- 1 cleverness cleverness  162 Sep  7 15:25 .bash_history
cleverness@Linux-Skillup-Challenge-Ubuntu:~$ man mkdir
cleverness@Linux-Skillup-Challenge-Ubuntu:~$ pwd
/home/cleverness
cleverness@Linux-Skillup-Challenge-Ubuntu:~$ mkdir /test
mkdir: cannot create directory &#x2018;/test&#x2019;: Permission denied
cleverness@Linux-Skillup-Challenge-Ubuntu:~$ sudo mkdir /test
cleverness@Linux-Skillup-Challenge-Ubuntu:~$ ls   
cleverness@Linux-Skillup-Challenge-Ubuntu:~$ sudo rm -rf /test
cleverness@Linux-Skillup-Challenge-Ubuntu:~$ mkdir ./test
cleverness@Linux-Skillup-Challenge-Ubuntu:~$ ls
test
cleverness@Linux-Skillup-Challenge-Ubuntu:~$ cd ./test
cleverness@Linux-Skillup-Challenge-Ubuntu:~/test$ 
</code></pre>]]></content:encoded></item><item><title><![CDATA[Ombi - Self Hosted Plex/Emby Content Request Application]]></title><description><![CDATA[<p>While surfing Reddit I came across someone who had this application listed in their docker apps, I googled it and instantly wanted it for my Plex server. &#xA0;Essentially allows other users that have access to your Plex server to search for content. &#xA0;If the content is already available</p>]]></description><link>https://cleverness.tech/ombi-self-hosted-plex-emby-content-request-application/</link><guid isPermaLink="false">63fc3b603940fd00015bb74b</guid><category><![CDATA[homelab]]></category><category><![CDATA[unraid]]></category><dc:creator><![CDATA[Jose Soto]]></dc:creator><pubDate>Sun, 07 Jun 2020 20:30:00 GMT</pubDate><media:content url="https://cleverness.tech/content/images/2023/02/ombi-1.png" medium="image"/><content:encoded><![CDATA[<img src="https://cleverness.tech/content/images/2023/02/ombi-1.png" alt="Ombi - Self Hosted Plex/Emby Content Request Application"><p>While surfing Reddit I came across someone who had this application listed in their docker apps, I googled it and instantly wanted it for my Plex server. &#xA0;Essentially allows other users that have access to your Plex server to search for content. &#xA0;If the content is already available it will show as such and even give a &quot;View on Plex&quot; link which opens Plex in another tab to the content(at the moment this works on majority of my library, but some links are broken), or if its not available they can request the media. &#xA0;It works with Jellyfin/Emby as well but at the moment I run Plex, so this seems like a efficient way to get family and friends to request stuff if I happen to be busy without me forgetting about it later when I get home.<br></p><figure class="kg-card kg-image-card"><img src="https://cleverness.tech/content/images/2023/02/ombi3-1.jpg" class="kg-image" alt="Ombi - Self Hosted Plex/Emby Content Request Application" loading="lazy" width="1920" height="1080" srcset="https://cleverness.tech/content/images/size/w600/2023/02/ombi3-1.jpg 600w, https://cleverness.tech/content/images/size/w1000/2023/02/ombi3-1.jpg 1000w, https://cleverness.tech/content/images/size/w1600/2023/02/ombi3-1.jpg 1600w, https://cleverness.tech/content/images/2023/02/ombi3-1.jpg 1920w" sizes="(min-width: 720px) 720px"></figure><p><br>The author has the main code hosted on his github which can be found quickly off his website at <a href="https://ombi.io/?ref=cleverness.tech">https://ombi.io/</a> but I went with the linuxserver.io image to put on my Unraid server. &#xA0;Installation is pretty simple, and since I&apos;m using Plex it let me log in with my Plex credentials and populated my Plex token and IP address, although since I run Plex in a container the port had to manually be changed to connect properly.</p><p>There&apos;s an option to import the user list from the Plex server if you are the admin there, and after that they can login with just their Plex credentials if you enable Plex OAuth in the settings. &#xA0;They have a mobile app on the Google Play Store which I ended up downloading since I wanted all request notifications to go through on my phone as a push notification. &#xA0;I think the app and admin access together cost 4-5 bucks but so far it seems to be worth it for what I need. &#xA0;After logging in on mobile my username eventually ended up on the mobile devices registered list under Settings &gt; Notifications &gt; Mobile Notifications and I was able to setup notifications, so now all requests will ping my phone. &#xA0;<br></p><figure class="kg-card kg-image-card"><img src="https://cleverness.tech/content/images/2023/02/ombi2.png" class="kg-image" alt="Ombi - Self Hosted Plex/Emby Content Request Application" loading="lazy" width="1920" height="1080" srcset="https://cleverness.tech/content/images/size/w600/2023/02/ombi2.png 600w, https://cleverness.tech/content/images/size/w1000/2023/02/ombi2.png 1000w, https://cleverness.tech/content/images/size/w1600/2023/02/ombi2.png 1600w, https://cleverness.tech/content/images/2023/02/ombi2.png 1920w" sizes="(min-width: 720px) 720px"></figure><p><br>Email took me abit because I am dumb. &#xA0;I made a gmail account for server email use and tried to register that and kept getting failed connection errors in the log. Double checked the google approved smtp ports and made sure IMAP was enabled on the gmail account and still an error. &#xA0;Of course the problem was a simple solution; I only ever opened the management port when launching the docker image, needed to relaunch it again with an additional port for the smtp email. &#xA0;After that it worked fine, test email and mass email/newsletter both worked.</p><p>There is sonarr/radarr/couchpotato integration here to automatically download stuff that I approve it seems but I haven&apos;t gone that far with this yet, only got it going today. &#xA0;After seeing how it goes over a period of time I&apos;ll probably look into that storage space permitting. &#xA0;App looks awesome so far though.</p>]]></content:encoded></item><item><title><![CDATA[Reading a folder with unknown number of files in Java]]></title><description><![CDATA[<p>One step of my second assignment in constructing a playlist involved reading in a list of csv files to construct the playlist from. &#xA0;I wanted to make a pretty comprehensive way to do it without listing all the files in the program incase we had to revisit this for</p>]]></description><link>https://cleverness.tech/reading-a-folder-with-unknown-number-of-files-in-java/</link><guid isPermaLink="false">63fc3a383940fd00015bb733</guid><category><![CDATA[java]]></category><dc:creator><![CDATA[Jose Soto]]></dc:creator><pubDate>Sun, 12 Apr 2020 08:22:00 GMT</pubDate><media:content url="https://cleverness.tech/content/images/2023/02/codingjava3.png" medium="image"/><content:encoded><![CDATA[<img src="https://cleverness.tech/content/images/2023/02/codingjava3.png" alt="Reading a folder with unknown number of files in Java"><p>One step of my second assignment in constructing a playlist involved reading in a list of csv files to construct the playlist from. &#xA0;I wanted to make a pretty comprehensive way to do it without listing all the files in the program incase we had to revisit this for the final assignment, or if I just wanted to reuse it for something else later on. &#xA0;So I went to look at the java docs for the File class we usually work with to see all the options, which you can see <a href="https://docs.oracle.com/javase/7/docs/api/java/io/File.html?ref=cleverness.tech"><strong>here</strong></a>.</p><p>Looking at all the methods the most useful one is the listOfFiles method, which returns an array of all the file names with their relative paths. &#xA0;Assuming all files we want read are being placed in a folder in the project directory called &apos;csvfiles&apos;, an example of this in use can be seen below.</p><pre><code> File folder = new File(&quot;csvfiles&quot;);
 File[] listOfFiles = folder.listFiles();
</code></pre><p>First line creates an object called folder of type File, and it&apos;s referencing the relative path of the csvfiles folder. &#xA0;Second line creates an array of objects called listofFiles of type File, and the listFiles method will return all the files in the folder and save it to the array. &#xA0;But let&apos;s say you want to either output all the files that are being read, or want to verify that the files are being read correctly.</p><pre><code>int numFiles = 0;
for (File file : listOfFiles) {
	if (file.isFile()) {
    	System.out.println(file);
        numFiles++;//counts number of files in directory
    }
}
</code></pre><p>We create an object file of type File, that will iterate as long as the array listOfFiles has values in it(in my case it will go 5 times). &#xA0;The isFile() method is essentially a boolean check, if it reads a proper file path it will execute, if not then it skips the if loop. &#xA0;We print out the file path and then with a counter variable created before the for loop we count the number of files total that are being read. &#xA0;Sample output for just this step should look something like this</p><pre><code>csvfiles\regional-global-weekly-2020-01-17--2020-01-24.csv
csvfiles\regional-global-weekly-2020-01-24--2020-01-31.csv
csvfiles\regional-global-weekly-2020-01-31--2020-02-07.csv
csvfiles\regional-global-weekly-2020-02-07--2020-02-14.csv
csvfiles\regional-global-weekly-2020-02-14--2020-02-21.csv
</code></pre><p>I&apos;m using the same sets of data that I talked about in <a>my last post</a> but including more than just the 1-17 to 1-24 sets of streaming data from Spotify. &#xA0;From here I&apos;ve validated the listofFiles array is valid and I can either use that to create a Queue for each one to later merge , or I can create a large Queue whose total size is that of all the files times the number of datasets per file which in the case of spotify csv&apos;s are 200.</p><pre><code>int size = 200;//Total number of songs in a spotify csv file
int trueSize = size * numFiles;//equal to 1000
</code></pre><p>The counter numFiles is not necessary depending on how you are looking to execute the reading of all the files into Queues, but it&apos;s an option and one to keep in mind depending on exactly what type of data you are looking to read and parse.</p>]]></content:encoded></item><item><title><![CDATA[Using OpenCSV to parse CSV files in Java]]></title><description><![CDATA[<p>Part of my degree path in Information Systems requires taking my programming classes in Java so far, and that includes Data Structures. &#xA0;Due to the nature of the course, majority of our assignments involve parsing data in a manner that we judge appropriate relative to the data we are</p>]]></description><link>https://cleverness.tech/using-opencsv-to-parse-csv-files-in-java/</link><guid isPermaLink="false">63fc25653940fd00015bb723</guid><category><![CDATA[java]]></category><dc:creator><![CDATA[Jose Soto]]></dc:creator><pubDate>Mon, 06 Apr 2020 08:38:00 GMT</pubDate><media:content url="https://cleverness.tech/content/images/2023/02/codingjava2.png" medium="image"/><content:encoded><![CDATA[<img src="https://cleverness.tech/content/images/2023/02/codingjava2.png" alt="Using OpenCSV to parse CSV files in Java"><p>Part of my degree path in Information Systems requires taking my programming classes in Java so far, and that includes Data Structures. &#xA0;Due to the nature of the course, majority of our assignments involve parsing data in a manner that we judge appropriate relative to the data we are working with and the task assigned. &#xA0;The first 2 assignments involved taking the Top 200 Streaming Songs list off Spotify and making first just a simple linked list that prints out, and then a playlist.</p><p>Spotifycharts.com makes it easy to export their data into a csv spreadsheet, there is a lovely button in the top right corner. &#xA0;The challenge that came with this first assignment is that a CSV file is short for comma-seperated values. &#xA0;If you download the CSV for lets say the week of 1-17-2020 to 1-24-2020 and open it, Windows defaults CSV files to excel and the data is nice and organize with no commas present. &#xA0;Right click and open it in Notepad and you&apos;ll see the actual raw data and see the column values seperated by commas on every line. &#xA0;So this essentially is where we had to decide how we wanted to read the data. &#xA0;Rather than try and fiddle with excel as I am not fond of it, I chose to just read the CSV data raw.</p><p><strong><a href="https://docs.oracle.com/javase/7/docs/api/java/lang/String.html?ref=cleverness.tech">Referencing the official java docs on the String class</a></strong> there is a method of the string class called split, where you can specify the regular expression you want it split by. &#xA0;For CSV the choice is obviously a comma. For normal pieces of data this would normally be enough and you might not need anything else to parse a CSV file, but due to the nature of music extra commas either in song titles or including other featured artists on the track aren&apos;t uncommon. &#xA0;A snippet of code of my first version of our first assignment in the main method.</p><pre><code>        TopStreamingArtists artistNames = new TopStreamingArtists();
        String csvFile = &quot;src/regional-us-weekly-2020-01-17--2020-01-24.csv&quot;;
        String inputLine;

        try {
            Scanner inputStream = new Scanner(new FileReader(csvFile));
            inputLine = inputStream.nextLine();
            while (inputStream.hasNextLine()) {
                inputLine = inputStream.nextLine();
                String[] tempArray = inputLine.split(&quot;,&quot;);
                int tempPosition = Integer.parseInt(tempArray[0]);
                String tempTrack = tempArray[1];
                String tempArtist = tempArray[2];
                int tempStreams = Integer.parseInt(tempArray[3]);
                String tempUrl = tempArray[4];

                artistNames.insertLast(tempPosition, tempTrack, tempArtist, tempStreams, tempUrl);
            }
            inputStream.close();
</code></pre><p>So the goal was to take my file and read it with a Scanner object I created, and read it one line at a time and save that line as a string in the variable inputLine, and then split it into an array of type String called tempArray at every comma so that I&apos;d have abunch of subStrings of that read line that I can then place in temp variables, and add to the linked list. &#xA0;First issue that came up is an error because the very top of the file has 2 lines that aren&apos;t necessary data we want to parse, just data relevant to reading the file if done so with the human eye. &#xA0;This can be solved by adding 2 &quot;inputLine = inputStream.nextLine();&quot; lines of code before executing the while loop but it&apos;s not really effective if you try to reuse this program later for some other pieces of data. &#xA0;What if you get a csv file with 30 lines of text before the data even appears, adding &quot;inputLine = inputStream.nextLine();&quot; 30 times is super messy.</p><p>If you ignore that or modify the CSV file to take those out, the second issue that came up around 85 lines in is that this code will literally separate at every comma as it&apos;s supposed to. &#xA0;So when the program got to Eminem&apos;s Yah Yah (feat. Royce Da 5&apos;9&quot;, Black Thought, Q-Tip &amp; Denaun), as you can see there are 2 extra commas in the track field alone. &#xA0;So the tempArray has more values in it that the previous lines did, and the positions will be all off, so then trying to ParseInt the 4th value in the array, instead of reading what should be the number of streams it will be reading Q-Tip &amp; Denaun and throw an error as this cannot be made into an integer value. &#xA0;The first assignment was flexible and we could manipulate the CSV file so I just submitted it with only the first 65 lines for grading. &#xA0;But considering everything else builds on this I wanted to make sure the second assignment was done properly, as it&apos;s literally using the same sets of data we used for the first one.</p><p>As our professor said either before or after the first assignment(I don&apos;t remember at this point, was roughly 2 months ago), there&apos;s no point in trying to reinvent the wheel in regards to doing something like reading CSV files. &#xA0;It&apos;s been something that has been down for a LONG time at this point and there are many available libraries that we can use and import into our java projects to assist with that. &#xA0;Some time spent googling and I came open OpenCSV. &#xA0;I downloaded the opencsv-5.1.jar file to get the library and it requires the Apache library as a dependency so I downloaded commons-lang3-3.9.jar. &#xA0;IntelliJ has a GUI for importing libraries but follow your IDE documentation for doing so if its different. &#xA0;After doing that I made a separate program of my first assignment that would utilize OpenCSV and imported the following 2 libraries up top to get the library loaded</p><pre><code>import com.opencsv.CSVReader;
import com.opencsv.exceptions.CsvValidationException;
</code></pre><p>So to see how that changes the code here&apos;s a snippet of the updated version for the first assignment</p><pre><code>        SortedArtists artistNames = new SortedArtists();
        String csvFile = &quot;src/regional-us-weekly-2020-01-17--2020-01-24.csv&quot;;

     try {

            CSVReader reader = new CSVReader(new FileReader(csvFile));
            String[] inputLine = reader.readNext();
            reader.skip(2);                    

            while ((inputLine = reader.readNext()) != null) {

                int tempPosition = Integer.parseInt(inputLine[0]);
                String tempTrack = inputLine[1];
                String tempArtist = inputLine[2];
                int tempStreams = Integer.parseInt(inputLine[3]);
                String tempUrl = inputLine[4];

                artistNames.insertLast(tempPosition, tempTrack, tempArtist, tempStreams, tempUrl);
            }
            reader.close();//stops reading file after while loop
</code></pre><p>CSVReader is a class in OpenCSV that is primarily used for this, **<a href="http://opencsv.sourceforge.net/apidocs/com/opencsv/CSVReader.html?ref=cleverness.tech">full java doc here</a>**to see all the methods. &#xA0;First important addition it added was the skip method, because by default it won&apos;t skip any lines like the first version and will read from the beginning, which will give the same error. &#xA0;By using the skip method and specifying the number of lines to skip, it will automatically do so and it keeps the code clean. &#xA0;I know that spotifycharts.com will always have the first 2 lines populated with information that I don&apos;t want to read, but if I wanted to reuse this code somewhere else and it needs to be modified I could change the 2 to whatever number was required, change it to a variable, even add in a user prompt to ask how many lines to skip if the program is going to be a multipurpose CSVReader program and save it to a variable and pass that into the skip method.</p><p>The second important addition which isn&apos;t immediately clear unless you open and read the CSVReader class and the readNext() method is it does all the parsing of the line and even removes quotes by default. &#xA0;It does all the proper parsing to account for things like extra commas and will split the line correctly, so running this version of the program I get a much cleaner output when displaying the linked list. &#xA0;Doing so made parsing data for the second assignment easier since it was essentially the same data sets.</p><p>Going forward I essentially learned to look for a library that does whatever is giving me trouble, as when it comes to Data Structures someone else probably had to deal with the same issue before and there&apos;s a library that takes care of it. &#xA0;Both versions of this program can be seen on my personal git repository for comparison <strong><a>right here</a>.</strong> &#xA0;Master version is what I submitted first, and version 2 branch is the updated one that uses OpenCSV to parse. &#xA0;The included CSV file was the edited one that doesn&apos;t have the first 2 lines so there is no reader.skip(2) code in version2 branch &#xA0;as I had placed here as an example, but that was used in the following program.</p>]]></content:encoded></item><item><title><![CDATA[Upgrading a ZFS Raid-1 Root Drive in Proxmox]]></title><description><![CDATA[<p>When first making my Proxmox server I just used drives I had left over from upgrading previous builds, I had the boot drives as a set of 120gb Sandisk SSDs, 2 Crucial 250gb SSDs as a ZFS Raid-1 holding my VMs, and a 250gb Corsair Force MP510 setup as a</p>]]></description><link>https://cleverness.tech/upgrading-a-zfs-raid-1-root-drive-in-proxmox/</link><guid isPermaLink="false">63fc22b57b806f00013cdf28</guid><category><![CDATA[homelab]]></category><category><![CDATA[proxmox]]></category><category><![CDATA[linux]]></category><dc:creator><![CDATA[Jose Soto]]></dc:creator><pubDate>Fri, 03 Apr 2020 20:00:00 GMT</pubDate><media:content url="https://cleverness.tech/content/images/2023/02/proxmox.jpg" medium="image"/><content:encoded><![CDATA[<img src="https://cleverness.tech/content/images/2023/02/proxmox.jpg" alt="Upgrading a ZFS Raid-1 Root Drive in Proxmox"><p>When first making my Proxmox server I just used drives I had left over from upgrading previous builds, I had the boot drives as a set of 120gb Sandisk SSDs, 2 Crucial 250gb SSDs as a ZFS Raid-1 holding my VMs, and a 250gb Corsair Force MP510 setup as a Directory running backups because I thought it would help the speeds when backing up my VMs(it really didn&apos;t). &#xA0;As using an NVMe drive in the onboard m.2 slot on the Gigabyte B450M Aorus motherboard limits the number of SATA ports to 4 I found myself running low on disks, so the plan is to migrate the boot mirror(which Proxmox calls rpool) to the 2 Crucial SSDs and run most of the VMs directly off that.</p><p><a href="https://pve.proxmox.com/wiki/ZFS:_Tips_and_Tricks?ref=cleverness.tech#Replacing_a_failed_disk_in_the_root_pool">The Proxmox official instructions</a> for replacing a failed disk in the root pool covered majority of the process, but I had to take a few extra steps as I was upgrading the size of the pool. Once I made sure all the VMs were backed up on the nvme I pulled one of the Sandisks and opened up the shell in proxmox. &#xA0;Since both of my boot drives were currently fine the pulled Sandisk served as my failsafe incase I messed anything up in the process, as it wouldn&apos;t be touched until the process was complete.</p><pre><code>  root@einherjar:~# zpool status -v
  pool: rpool
  state: ONLINE
  scan: resilvered 63.5G in 0 days 00:27:32 with 0 errors on Sat Apr  4 09:24:53 2020
  config:
  NAME        STATE     READ WRITE CKSUM
    rpool       ONLINE       0     0     0
      mirror-0  ONLINE       0     0     0
        sdd3    UNAVAILABLE  0     0     0
        sdc3    ONLINE       0     0     0
</code></pre><p>Whatever disk you pull is going to show up unavailable. &#xA0;They show up here as partitions /dev/sdd3 and /dev/sdc3 but disk id&apos;s can also show up, just go off whatever your pool status shows. &#xA0;After identifying the pulled drive it needs to be put offline in the pool, run status again to confirm.</p><pre><code>  root@einherjar:~# zpool offline rpool /dev/sdd3
  root@einherjar:~# zpool status -v
  pool: rpool
  state: DEGRADED
  status: One or more devices has been taken offline by the administrator.
        Sufficient replicas exist for the pool to continue functioning in a
        degraded state.
  action: Online the device using &apos;zpool online&apos; or replace the device with
        &apos;zpool replace&apos;.
  scan: resilvered 63.5G in 0 days 00:27:32 with 0 errors on Sat Apr  4     09:24:53 2020
  config:
  NAME        STATE     READ WRITE CKSUM
    rpool       ONLINE       0     0     0
      mirror-0  ONLINE       0     0     0
        sdd3    OFFLINE      0     0     0
        sdc3    ONLINE       0     0     0
</code></pre><p>From here plug the replacement drive in(my Crucial 250 in this case) and get the disk location either in the Proxmox GUI or by running fdisk -l in the shell(<strong>we&apos;ll go with /dev/sdb for the guide</strong>)</p><p>Next step was to copy the partition table over from the current Sandisk 120gb SSD to the crucial. &#xA0;As the official wiki stresses, <strong>the order the drives are placed in the sgdisk command are important as doing it wrong will wipe your current boot disk and that pool, taking all the proxmox configs with it</strong>.</p><p>sgdisk --replicate=/dev/target /dev/source</p><p>So the target disk goes first, and the source second. &#xA0;In the shell in my case it goes</p><pre><code>  root@einherjar:~# sgdisk --replicate=/dev/sdb /dev/sdc
</code></pre><p>This should copy the partitions over. &#xA0;Now at this step if you are like me and upgrading the disks to bigger size, you will need to resize the partitions of the drive(which in my case in Proxmox is sdb3) as its copying over a 120gb partition over and that means we have roughly 130gb of free space on this drive that won&apos;t be used otherwise. &#xA0;I took the wikis advice and used parted to resize it, which I had to install.</p><p>Run parted first on your current root disk to see something similar to the following</p><pre><code>root@einherjar:~# parted /dev/sdc
GNU Parted 3.2
Using /dev/sdc
Welcome to GNU Parted! Type &apos;help&apos; to view a list of commands.
(parted) print                                                            
Model: ATA CT250MX500SSD1 (scsi)
Disk /dev/sdc: 250GB
Sector size (logical/physical): 512B/4096B
Partition Table: gpt
Disk Flags: 

Number  Start   End     Size    File system  Name  Flags
 1      17.4kB  1049kB  1031kB                     bios_grub
 2      1049kB  538MB   537MB   fat32              boot, esp
 3      538MB   249GB   248GB   zfs
</code></pre><p>Of note is the disk size in line 7, and the end sector for partition 3 in line 15 as that is the partition we are going to resize. &#xA0;Run parted or fdisk on your new disk to verify the disk size and then run the following commands</p><pre><code>root@einherjar:~# parted /dev/sdb
GNU Parted 3.2
Using /dev/sdd
Welcome to GNU Parted! Type &apos;help&apos; to view a list of commands.
(parted) resizepart                                                            
Partition number? 3
</code></pre><p>Then specify the size, which for me I input 248GB but in actuality it should have been 249GB, as looking at the free space available on the disk in fdisk I left 1gb out. &#xA0;So now the zfs pool partition on the disk is the correct size.</p><pre><code>root@einherjar:~# sgdisk --randomize-guids /dev/sdb
The operation has completed successfully.
</code></pre><p>Wiki suggests to do this to avoid confusion in the server, and I didn&apos;t want to deal with a kernel panic so there it goes. &#xA0;And next step which didn&apos;t work for me</p><pre><code>grub-install /dev/sdb
</code></pre><p>If this completes for you, great. &#xA0;If it doesn&apos;t, you need to fix this otherwise the drive won&apos;t be able to boot the OS as I came to find out. &#xA0;Easiest way I got this going was to just copy the working bios_grub and EFI partitions off the currently working boot drive, which are partitions 1 and 2. &#xA0;Thankfully after running sgdisk command the new drive has the same partitions in the same start sectors</p><pre><code># dd if=/dev/sdc1  of=/dev/sdb1 
</code></pre><p>The above command tells dd to use /dev/sdc1 as input file and write it to output file /dev/sdb1. &#xA0;Do this again for partition 2, and it should be able to properly boot now.</p><p>We can now replace the pulled sandisk with the crucial drive in the rpool. &#xA0;Syntax for the command is</p><pre><code>zpool replace &lt;pool&gt; &lt;device&gt; [new-device]
</code></pre><p>Where first device is the name of the drive you pulled, and new device is the one we&apos;ve been formatting all this time. &#xA0;Again you need to write down the device exactly as it shows up in zpool status -v, so if the pool is listing disk-id&apos;s or UUIDs for either drive we are swapping go with that or it won&apos;t work.</p><pre><code>root@einherjar:~# zpool replace rpool /dev/sdd3 /dev/sdb3
Make sure to wait until resilver is done before rebooting.
</code></pre><p>You might need a -f flag at the end of the replace line, if the shell prompts for it then add it in as I needed to do for this first swap. &#xA0;This will start the resilvering process where the data from the current root drive gets mirrored over to the new root drive. &#xA0;Do not power down the server until this is done. &#xA0;Check on the status and you should see a similar output to below depending on your drives/pool size</p><pre><code>  root@einherjar:~# zpool status -v
  pool: rpool
  state: DEGRADED
  status: One or more devices is currently being resilvered.  The pool will
	continue to function, possibly in a degraded state.
  action: Wait for the resilver to complete.
  scan: resilver in progress since Sat Apr 4 09:27:53 2020
  	27G scanned out of 64G at 4.46M/s, 10m to go
    26G resilvered, 40.60% done
  config:
  NAME        STATE     READ WRITE CKSUM
    rpool       ONLINE       0     0     0
      mirror-0  ONLINE       0     0     0
	    sdc3         ONLINE       0     0     0
	    replacing-1  OFFLINE      0     0     0
	      sdd3       OFFLINE      0     0     0
	      sdb3       ONLINE       0     0     0  (resilvering)
</code></pre><p>When its finally done should see something similar to</p><pre><code>root@einherjar:~# zpool status -v
  pool: rpool
 state: ONLINE
  scan: resilvered 63.5G in 0 days 00:27:32 with 0 errors on Sat Apr  4 09:37:53 2020
config:

        NAME        STATE     READ WRITE CKSUM
        rpool       ONLINE       0     0     0
          mirror-0  ONLINE       0     0     0
            sdb3    ONLINE       0     0     0
            sdc3    ONLINE       0     0     0
</code></pre><p>Power the system off and pull the old root drive and power it on to make sure the new drive boots and all your server settings are the same. &#xA0;If it did then great, next step is to repeat everything all over again with the 2nd new drive to copy everything over and resilver the pool again. &#xA0;This will give you a new boot pool with no errors in the zpool.</p><p>Last step is to turn on autoexpand for the pool, otherwise in my case I am stuck with 120gb available for use with VMs when the disks are obviously much bigger than that now. &#xA0;Running zpool list will confirm whats actually available for use. &#xA0;A very simple</p><pre><code>root@einherjar:~# zpool set autoexpand=on rpool
</code></pre><p>As rpool is the name for the boot pool in Proxmox will turn autoexpand on(which when checking before was default to off) and running zpool list should now show a pool size almost equal to the size of your drives(barring a few MB for the boot and EFI partition)</p><pre><code>root@einherjar:~# zpool list
NAME    SIZE  ALLOC   FREE  CKPOINT  EXPANDSZ   FRAG    CAP  DEDUP    HEALTH  ALTROOT
rpool   231G  87.4G   144G        -         -     2%    37%  1.00x    ONLINE 
</code></pre><p>With that my boot pool has now been upgraded and I have some more flexibility in my setup. &#xA0;After migrating the VMs over from the NVMe drive to the local-zfs pool on the boot drives I used fdisk to nuke it and rebooted it so changes would take effect, and then created a LVM storage on it to run VMs that can take advantage of the higher speeds of the drive, or ones that would be negatively affected by other VMs on the local-zfs storage causing an I/O delay. &#xA0;Anything like a Minecraft server or music/video server that is handling things in real time can suffer delays if even SSDs are busy with things like scheduled backups or heavy writes from 1 or more of the VMs running on it. &#xA0;Moving it to a separate drive will reduce that and most game servers in particular benefit from running off an NVMe drive when its available.</p><p>I replaced one of the Sandisks with a 1TB Hard Drive I pulled from a PS4 Pro when I upgraded it to a 2TB drive and made a new Directory called Backup with that, and even though its a 5400RPM drive I was still getting a constant 160MB in read/writes when backing up from the SSDs, and alittle faster when backing up from the VMs on the NVMe. &#xA0;Rebuilding is abit slower from it but since this is for home use it&apos;s a good trade-off in exchange for more space to hold backups. &#xA0;The 4th slot in my drive cage is just occupied by a sandisk I formatted empty for airflow purposes, but I already plan to replace it with another SSD later on when I take on my next hardware upgrade for the Proxmox server in trying to setup a separate Windows VM with GPU passthrough to play certain games I won&apos;t have access to when I migrate my desktop OS to Linux again.</p><p>I had an original boot disk and backups of all my VMs on a separate Directory so this was a good learning experience in managing zpools without much fear of losing everything. &#xA0;Since Proxmox is essentially Debian at its core the process carries over to other Linux distros that support ZFS so it&apos;s at least something that I can call back on going forward.</p>]]></content:encoded></item></channel></rss>