List of Blog Posts

Internet and Safety: Why Physical Businesses Should Not Require Everyone To Use Internet?

(For my blog post, I want to focus on the audience regarding people who use Internet every single day and knows a lot about cybersecurity. Myself included.)

Imagine a scenario: you went to get your haircut and the place you went to requires you to enter an email address before you get your haircut. Why? Even if I do have a smartphone and I use Internet every single day, why must I put in my email address? For what purpose? To send spam? For businesses, they might say “we respect your privacy and take security seriously,” but in my mind, I would say that if an email gets compromised in a data breach, it’s more likely that those who are not tech-savvy are more likely to receive spam and phishing emails. Not thinking about security when using the Internet can lead to ransomware and identity theft. They might stop using the computer altogether because of fear of feeling unsafe online.

Okay, so I can imagine people asking…

What is ransomware?

So anyone who have not used the Internet before would then ask…

Okay, so what is malware? Oh, maybe I should click in the link. Oh, and what is a file?

Okay, I can imagine tech-savvy folks asking “what do you mean, ‘what is a file?’ Do you ever know how to use a computer before?” How can we guide people who does not use Internet every single day, let alone not knowing “what is an Internet?”

What is an operating system? Windows? Mac? Linux? What is an email address? What is a “file?” See where I’m going with? What is Android? iPhone? iOS? How do I manage files and folders in my computer? How do I check my email? I hope you get my point.

So back to the topic about email address requirement, people who have no plans to educate themselves regarding security and privacy should not have an email address and should not be using the Internet. Even a smartphone can be very complex compared to a cell phone that only make and receive phone calls and nothing else. Let alone how to send and read text messages. And yes, I’m talking about people who use cell phones with no capability for browsing the Internet. Not even Firefox, Chrome, or Safari.

Okay, so you say that your 90-year-old grandmother knows how to use the Internet, takes care of security themselves, and I should not overly-generalize myself. Well, that’s great. People should be educated regarding the implications regarding cyber attacks and how to protect themselves; however, as long as people out there (Demographics of Cybercrime Report) do not take their time to educate and protect themselves, businesses should not require them to have an email address when they check in. Even dentists should make email address requirement optional as well. Even though I have close to 200 email addresses (one email address per site with no plus addressing and no catchall for my domain), I do not want to enter my email address if I do not want to for privacy and security reasons.

Businesses say “we take security and privacy seriously,” yet businesses do not take their time to harden and patch their systems over time. Of course, training employees regarding how to protect themselves against phishing emails is a very important part of having a security culture for businesses. But then again, an email address would be a requirement for businesses for getting your customers to setup an account online, but in a physical world where people simply walk in, as long as people do not use the Internet and do not plan to educate themselves, an email address should not be a requirement. At all.

My Dream Home of the Future: Computer in Server Closet; KVM in Home Office; Home Theater

I have been watching a couple of YouTube videos of people who want a computer in one room (such as a wiring closet) and a keyboard, video, and mouse (KVM) in a home office. To give you an idea of what I’m talking about, I want to post links to YouTube videos.

Embedding YouTube or Odysee videos will insert a tracking cookie in users’ personal computers. As a citizen of the US, I need to follow GDPR if European visitors visit my website. I don’t like and want to talk to lawyers to be honest. 🤣😀

As for the video from Linus Tech Tips, I would much rather have a couple of computers rather than single computer that can house a couple of virtual machines running desktop OSes such as Linux and Windows just to make it easier for me. So yeah, a virtual machine is a computer within a computer that can serve different purposes such as running Ubuntu within Windows using VirtualBox or by running Windows OS in a Linux host using KVM or Xen.

(more…)

Mic Comparison: Shure MX185 Cardioid vs Movo LV8-D Omni-Directional Lavalier Microphone

Here’s a link to a video in Odysee:

Mic Comparison: Shure MX185 Cardioid vs Movo LV8-D Omni-Directional Lavalier Microphone

Embedding any videos from any external sources will insert tracking cookies in your computer or mobile device so I decided to link a video instead. Even in the US, I have to comply with Europe’s GDPR as I want to allow all visitors to visit my site. Inserting any kind of tracking cookies is against my privacy policy. I would like to upload my videos to my website; however, videos take up a lot of space and that’s why I uploaded my video regarding the mic comparison to Odysee.

This is a comparison of two lavalier microphones. Recently, I bought a Shure MX185 cardioid lavalier microphone as I want to test if a uni-directional (cardioid) microphone is right for me, especially if I want to test and hear if my AKG K702 headphone leaks sound to my microphone especially for the Zoom meeting. I bought a Movo LV8-D microphone as of late October so I can participate in Zoom meeting that began November of last year. The Zoom meeting I am participating in is Cisco Academy from National Industries for the Blind. I’m studying for Cisco Certified Network Associate certificate (CCNA, for short) and my class ends by the end of August. I asked if students and my instructor can hear any leaks coming from my K702 headphone and they said they did not hear any leaks at all, which is great. However, I have a Sony WX1000XM3 headphone and because of the shape of my headphone, I don’t think my hearing aids are picking up any high frequency sounds unlike when I use my AKG open-back headphone.

I plan to ship my Shure microphone back because the uni-direction nature of a lavalier microphone is not for me, especially as I was reading from left to right as i read the script during the recording.

Do note that even though I did cut out a couple of pauses in my audio production software (Ardour), I tend to speak slow as speaking at a moderate speed for more than a minute is not my second nature. As I live in Altha, FL, a rural town in the United States, I’ve been very lonely a lot even when I go to restaurants with my family. Plus, I did not position the text inside the dialog in the first part of the video correctly. I do not want to waste another 45+ minutes trying to render the entire video using Blender. Although as a Linux user, I could have used KDenLive instead of Blender; however, as Blender is a very easy tool for me to use, I used it for the majority of my video editing. My familiarity with KDenLive is secondary to Blender.

When I zoom in using GNOME Magnifier (Windows key+Alt+8 to activate the magnifier and Windows key+Alt+- or Windows key+Alt+= to zoom in or out, respectively), there is a small mouse cursor shown in the screen. I think it’s a bug with the compositor that draws the entire application, be it Firefox, GIMP, or Ardour). Please ignore the small mouse cursor. Thanks.

Anyway, I appreciate you checking out my video that I linked above. Here are the links to products listed for the video:

When Uploading A Screenshot of a Website, Be Aware Of Your Browser Tabs

When uploading a screenshot of your website (or someone else’s website), make sure your email address (or portion of your email address) is not exposed when taking a screenshot. I uploaded my screenshot of pagination for my website and a part of my email address has been exposed over the web and I had to retake the screenshot without it. If you have a webmail opened in one browser tab such as GMail/Google Workspace, your browser tab will look similar to this:

Inbox (5): yourname(at)your…

That tab is exposed by the <title> tag inside a website. Here’s what I mean:

<html>
  <head>
    <title>Inbox (5): yourname(at)yourdomainname(dot)com</title>
  <head>
  <body>
    <h1>Your E-Mail Provider</h1>
    <p>Your email messages go here.</p>
  </body>
<html>

Instead of “@”, I use “(at)” so that spam harvesters and bots won’t harvest any email addresses in my website; however, I won’t give away any of my 170+ email addresses at all. Pay special attention to the title of web pages that you currently have opened. By “title,” I meant your browser tabs. My advice is do not leave anything sensitive unattended. I hope I can be of help to everyone. Be safe out there in the web!

New Addition To My Website: Pagination

As a web developer of my website, I have implemented pagination that allows anyone to view more posts by page and be able to view blog posts by month and year. I created a custom theme from scratch so that I can personalize my website to my liking. I wanted to give the pagination system an “electronic” look.

Pagination along with month and year for my website
This screenshot shows pagination implemented in my website. In my development machine, I have set the number of posts per page to 5 in order to demonstrate the effect. I blurred the surrounding image to cut the file size by half.

For those with eyesight, you can click in the image to see a full screen of my desktop that shows the pagination system in effect.

Here’s the code if any web developers want to implement it into their WordPress/ClassicPress website. I grabbed and modified the code from the kriesi.at website and once I got it done, I then wanted to add a month/year functionality into my pagination system. Even though I did seek help from the ClassicPress forum in my thread regarding getting the latest archive in an array instead of a link, I was able to do it myself with the help of this webpage that contained the function called wp_get_archives(). Here is a code taken from the wordpress.org site.

if ( 'monthly' == $r['type'] ) {
    $query = "SELECT YEAR(post_date) AS `year`, MONTH(post_date) AS `month`,"
        ." count(ID) as posts FROM $wpdb->posts $join $where"
        ." GROUP BY YEAR(post_date), MONTH(post_date) ORDER BY post_date $order $limit";
    $key = md5( $query );
    $key = "wp_get_archives:$key:$last_changed";
    if ( ! $results = wp_cache_get( $key, 'posts' ) ) { 
        $results = $wpdb->get_results( $query );
        wp_cache_set( $key, $results, 'posts' );
    }   
    if ( $results ) { 
        $after = $r['after'];
        foreach ( (array) $results as $result ) { 
            $url = get_month_link( $result->year, $result->month );
            /* translators: 1: month name, 2: 4-digit year */
            $text = sprintf( __( '%1$s %2$d' ), $wp_locale->get_month( $result->month ), $result->year );
            if ( $r['show_post_count'] ) { 
                $r['after'] = ' (' . $result->posts . ')' . $after;
            }
            $output .= get_archives_link( $url, $text, $r['format'], $r['before'], $r['after'] );
        }   
    }   
}

So I looked over the code and I saw that there is a $result variable that is converted into an array. I took that code from the WordPress.org website and I modified the code to suit my needs in functions.php inside my custom theme folder.

function monthly_archive_array() {
    global $wpdb;
    $r['type'] = 'monthly';
    $where = apply_filters( 'getarchives_where',
        "WHERE post_type = 'post' AND post_status = 'publish'", $r );

    $last_changed = wp_cache_get( 'last_changed', 'posts' );
    if ( ! $last_changed ) {
        $last_changed = microtime();
        wp_cache_set( 'last_changed', $last_changed, 'posts' );
    }

    /**
     * Filter the SQL JOIN clause for retrieving archives.
     *
     * @since 2.2.0
     *
     * @param string $sql_join Portion of SQL query containing JOIN clause.
     * @param array  $r        An array of default arguments.
     */
    $join = apply_filters( 'getarchives_join', '', $r );
    
    $query = "SELECT YEAR(post_date) AS `year`, MONTH(post_date) AS `month`, count(ID) as posts FROM $wpdb->posts $join $where GROUP BY YEAR(post_date), MONTH(post_date) ORDER BY post_date ASC";
    $key = md5( $query );
    $key = "wp_get_archives:$key:$last_changed";
    if ( ! $results = wp_cache_get( $key, 'posts' ) ) {
        $results = $wpdb->get_results( $query );
        wp_cache_set( $key, $results, 'posts' );
    }
    if ( $results ) {
        return (array)$results;
    }
}

$archiveMonthlyList = monthly_archive_array();

function get_monthly_archive_array() {
    global $archiveMonthlyList;
    return $archiveMonthlyList;
}

Note that in the last 6 lines of code, I decided to have a function (get_monthly_archive_array()) get the result from a variable ($archiveMonthlyList). When I go to my website, ClassicPress executes a functions.php file in my custom theme folder so that I don’t get the data from the database twice. Yet I actually do have wp_get_archives() in my sidebar, so I did have my website execute the same SQL statement twice. Well, one is in descending order from the newest to the oldest in the sidebar and I wanted to get the month and year of all the published posts from oldest to newest. If I replace the built-in function in my sidebar with my own function which exposes an array, I should be able to improve performance for my website, although probably not by much.

function constructLinkFromYearMonth($array, $index, $nextMonth) {
    $offset = ($nextMonth === true) ? 1 : -1;
    $spanlsaquote =  ($offset === -1) ? "<span class='visualonly'>‹ </span>" : "";
    $spanrsaquote =  ($offset ===  1) ? "<span class='visualonly'> ›</span>" : "";
    $prevNextMonth = ($offset === -1) ? "Previous" : "Next";
    echo "<a href='".get_month_link($array[$index + $offset]->year,$array[$index + $offset]->month )
        ."'>".$spanlsaquote."<span class='screenreader'>".$prevNextMonth." month: </span><span class='narrow-screen'>"
        .showMonthYearLocale(
        [$array[$index + $offset]->year,$array[$index + $offset]->month])."</span>".$spanrsaquote."</a>";
}

function show_pagination() {
    global $paged;
    if(empty($paged)) $paged = 1;
    
    global $wp_query;
    $pages = $wp_query->max_num_pages;
    if(!$pages)
    {
        $pages = 1;
    }

    echo "<div class='pagination'>";
    echo "<h3>View More Posts By Month or Page</h3>";
    echo "<div class='pagination_area'>";

    echo "<div class='pagination_top'>";

    // Get the list of months and years from the archive in an array.
    $monthlyArchive = get_monthly_archive_array();
    // If there is year/month in URL, get it and trim the leading and
    // trailing slashes. Example: 2021/04
    $currentMonthYear = trim($_SERVER['REQUEST_URI'],'/');
    // array[0] = year, array[1] = month
    // Example: array[0] = "2021", array[1] = "04"
    $curMonthYearArray = explode('/',$currentMonthYear);
    if(preg_match("/^[0-9]{4}\/(0[1-9]|1[0-2])$/",$curMonthYearArray[0].'/'.$curMonthYearArray[1])) {
        // array[0] = year, array[1] = month
        // Example: array[0] = "2021", array[1] = "04"
        $curMonthYearArray = explode('/',$currentMonthYear);
        // Initialize a blank array for integers.
        $intCurMonthYearArray = Array();
        // Convert strings to integers in an array in a new variable.
        foreach($curMonthYearArray as $curMonthYear)
            $intCurMonthYearArray[] = (int)$curMonthYear;
        // Initialize the integer for the index.
        $indexOfMonthYearArray = 0;
        foreach ($monthlyArchive as $key => $val) {
           if ((int)$val->year === $intCurMonthYearArray[0] &&
               (int)$val->month === $intCurMonthYearArray[1]) {
               $indexOfMonthYearArray = $key;
           }
        }

        echo "<ul class='pagination_month'>";
        echo "<li class='month_current'>";
        echo "<span>".showMonthYearLocale($curMonthYearArray)."</span>";
        echo "</li>";
        if($indexOfMonthYearArray > 0) {
            echo "<li class='month_prev'>";
            constructLinkFromYearMonth($monthlyArchive, $indexOfMonthYearArray, false);
            echo "</li>";
        } else echo "<li class='month_prev smallfontsize'><a class='screenreader'>Beginning of current month</a></li>";
        if($indexOfMonthYearArray + 1 < count($monthlyArchive)) {
            echo "<li class='month_next'>";
            constructLinkFromYearMonth($monthlyArchive, $indexOfMonthYearArray, true);
            echo "</li>";
        } else echo "<li class='month_next smallfontsize'><a class='screenreader'>End of current month</a></li>";
        echo "</ul>";
    } else {
        $latest = $monthlyArchive[count($monthlyArchive) - 1];
        echo "<div class='pagination_month msg'><span>View latest posts since "
            ."<a class='date-narrow' href='".get_month_link($latest->year,$latest->month )
            ."'>".showMonthYearLocale([$latest->year,$latest->month])."</a>.</span></div>";
    }

    echo "</div>";

    if(1 != $pages)
    {
        echo "<div class='pagination_bottom'>";
        echo "<div class='pagination_prevbtns'>";
        if($paged > 2)
            echo "<a href='".get_pagenum_link($paged - 2)."'>«</a>";
        else echo "<a class='visualonly pagination_disbtn'>«</a>";
        if($paged > 1)
            echo "<a class='pageprev' href='".get_pagenum_link($paged - 1)."'>‹</a>";
        else echo "<a class='pageprev visualonly pagination_disbtn'>‹</a>";
        echo "</div>";

        echo "<ul class='pagination_slot'>";
        for ($i=1; $i <= $pages; $i++)
        {
            echo "<li>";
            echo ($paged == $i)? "<a class='pagination_number current'><span class='screenreader'>Current Page: </span>".$i."</a>":"<a href='".get_pagenum_link($i)."' class='pagination_number' >".$i."</a>";
            echo "</li>";
        }
        echo "</ul>";

        echo "<div class='pagination_nextbtns'>";
        if ($paged < $pages)
            echo "<a class='pagenext' href='".get_pagenum_link($paged + 1)."'>›</a>";  
        else echo "<a class='pagenext visualonly pagination_disbtn'>›</a>";
        if ($paged < $pages-1)
            echo "<a href='".get_pagenum_link($paged + 2)."'>»</a>";
        else echo "<a class='visualonly pagination_disbtn'>»</a>";
        echo "</div></div></div></div>\n";
    } else {
        echo "<div class='pagination_bottom'>";
        echo "<div class='pagination_prevbtns'>";
        echo "<a class='visualonly pagination_disbtn'>«</a>";
        echo "<a class='pageprev visualonly pagination_disbtn'>‹</a>";
        echo "</div>";
        echo "<div class='pagination_slot msg'><span class='nopages'>Only 1 page shown.</span></div>";
        echo "<div class='pagination_nextbtns'>";
        echo "<a class='pagenext visualonly pagination_disbtn'>›</a>";
        echo "<a class='visualonly pagination_disbtn'>»</a>";
        echo "</div></div></div></div>\n";
    }
    
}

function monthly_archive_array() {
    global $wpdb;
    $r['type'] = 'monthly';
    $where = apply_filters( 'getarchives_where',
        "WHERE post_type = 'post' AND post_status = 'publish'", $r );

    $last_changed = wp_cache_get( 'last_changed', 'posts' );
    if ( ! $last_changed ) {
        $last_changed = microtime();
        wp_cache_set( 'last_changed', $last_changed, 'posts' );
    }

    /**
     * Filter the SQL JOIN clause for retrieving archives.
     *
     * @since 2.2.0
     *
     * @param string $sql_join Portion of SQL query containing JOIN clause.
     * @param array  $r        An array of default arguments.
     */
    $join = apply_filters( 'getarchives_join', '', $r );
    
    $query = "SELECT YEAR(post_date) AS `year`, MONTH(post_date) AS `month`, count(ID) as posts FROM $wpdb->posts $join $where GROUP BY YEAR(post_date), MONTH(post_date) ORDER BY post_date ASC";
    $key = md5( $query );
    $key = "wp_get_archives:$key:$last_changed";
    if ( ! $results = wp_cache_get( $key, 'posts' ) ) {
        $results = $wpdb->get_results( $query );
        wp_cache_set( $key, $results, 'posts' );
    }
    if ( $results ) {
        return (array)$results;
    }
}

$archiveMonthlyList = monthly_archive_array();

function get_monthly_archive_array() {
    global $archiveMonthlyList;
    return $archiveMonthlyList;
}

The reason why I decided to remove the range variable is so I want viewers in mobile devices to be able to scroll through pages horizontally (left/right). Of course, for desktop users I could add some JavaScript code that allows me to add/remove numbers based on the current page number and based on the width of the web browser. However, I am a heavy proponent of not using JavaScript whenever possible. I want everyone without JavaScript or for those such as me who use NoScript to enjoy the full potential of my website.

Web development is hard work, but at the end of the day, I enjoyed it a lot. Thanks for reading and enjoy visiting my site!

And note to self: I need to encode HTML code even if it’s inside a <pre> tag before I break my website when publishing my post. Don’t forget to do the same if you are a web developer as well. Use &lt; for < and &gt; for >. You can also use &quot; for " as well. View the source code for my post to see what I did. In Firefox, open the context menu and choose “View Page Source.” Same goes for Google Chrome.

Updated as of July 3, 2021 at 12:44 AM: Added <wbr /> tag to a long function in order to break the function into two lines. This is useful for mobile devices. All today’s mobile devices should support HTML5 by now.

From Optimists To Optimists: Disney Imagineers and Tomorrowland

After I watch TRON: Legacy tonight (Friday night is my pizza/movie night), I did a search for the following in Google:

tomorrowland tron legacy wall-e optimist

I came across a headline that talks about Disney imagineers and Tomorrowland.

And below the article in the search result is my tweet about Star Trek and Netflix (and yes, you should read my tweets below my main tweet even if you do not have a Twitter account).

In the article about the movie Tomorrowland, I found it interesting that Disney created a game called “The Optimist.” It seems like the movie Tomorrowland debuted 2 years after the events of “The Optimist.” If you watch the movie “Tomorrowland,” you should hear that name called “Plus Ultra” and read what Plus Ultra is about. That is before Frank Walker, Casey Newton, and Athena stepped into the rocket in order to travel to another dimension, Tomorrowland.

I will always keep my spirit of optimism going for as long as I live. If only Tomorrowland wasn’t such an underrated movie.

My Comment To: 10 Tips for Hardening your Linux Servers

Update as of Friday, April 23, 2021

YouTube’s spam filtering algorithms censored my script below and whenever I posted a link to my comment in my website, YouTube automatically deleted my comment containing a link. That’s YouTube’s censorship at work for you. We Linux users have been censored off of YouTube for good. We should all thank YouTube for limiting our free speech and hindering us for openly contribute what we want to share. Please, content creators, for goodness sake, please do us viewers and contributors a favor and sync your YouTube videos to Odysee. Thank you.

Original

(This post is a comment to a YouTube video: 10 Tips for Hardening your Linux Servers)

I can follow all 8 tips although the last 2 tips are for businesses. Plus, I’m using crontab to help me backup my ClassicPress website from my VPS server.

Crontab:

0 0 * * * /home/[REDACTED]/bin/backup-cp.sh

Script (~/bin/backup-cp.sh)

#!/bin/sh
ssh -p [REDACTED] [REDACTED]@graysonpeddie.com -i ~/.ssh/classicpress ~/bin/cpbackup.sh > ~/cpbackup/classicpress-$(date +%Y%m%d).sql
ssh -p [REDACTED] [REDACTED]@graysonpeddie.com -i ~/.ssh/classicpress tar czf - /var/www > ~/cpbackup/classicpress-$(date +%Y%m%d).tar.gz > /dev/null 2>&1
scp -P [REDACTED] -i ~/.ssh/classicpress [REDACTED]@graysonpeddie.com:/etc/apache2/sites-enabled/000-default.conf ~/cpbackup/classicpress-apache-$(date +%Y%m%d).conf

The “[REDACTED]” is for obscuring the port number and username. And yes, I have had problems with YouTube deleting my comment when I was going to edit it because I think YouTube’s algorithms thought I posted spam but I did not. So there are times when I have to outsmart computer algorithms.

How To Create a New User in pfSense and VyOS?

When you setup your new router, it’s always a good idea to create a new user other than admin for pfSense and vyos for VyOS in order to reduce the chance that bots and miscreants will gain access to your router.

VyOS

Here’s the completed configuration of my VyOS router and I will show you the commands.

Configuration
service {
    # ...
    ssh {
        access-control {
            allow {
                user <username>
                user vyos
            }
        }
        listen-address 10.249.0.1
    }
}
system {
    # ...
    login {
        banner {
            pre-login "Unauthorized access is strictly prohibited."
        }
        user <username> {
            authentication {
                encrypted-password ****************
                plaintext-password ****************
            }
            full-name "First and last name goes here."
            home-directory /home/<username>
        }
        user vyos {
            authentication {
                encrypted-password ****************
                plaintext-password ****************
            }
        }
    }
# ...
}
Commands
ssh vyos@10.249.0.1
configure
edit system login user <username>
set authentication plaintext-password <your-password-goes-here>
set full-name "First and last name goes here."
set home-directory /home/<username>
exit
edit service ssh access-control
set allow user <username>
set allow user vyos
commit
save

You want to allow vyos access using SSH to make sure it works. Also, there is encrypted-password in VyOS but VyOS gave me an error telling me that the encrypted password is invalid. I did try to discard, but VyOS told me there are not changes to be discarded, so I saved, started a new terminal window, and once I SSH into my VyOS router for 10.249.1.1, everything works fine.

Now don’t exit out of VyOS session just yet. You want to make sure SSH is working properly for a user you want to log into. Because otherwise editing and viewing the configuration will have to be done either through the use of a console cable or a monitor and keyboard hooked up to a monitor. SSH using your new username and password you’ve created. If you can successfully login to VyOS with a different username, you can simply remove the vyos user from the access control list in configuration mode.

delete service ssh access-control allow user vyos

Again, stay logged in to VyOS and use a different terminal to test and make sure you can log into VyOS through SSH. If everything is working as intended, you can safely log out of VyOS from all the terminals you’ve opened.

Also, you can configure a banner. Examine the configuration above and see if you can add a login banner. The pre-login is for when a user attempts to access the VyOS router using SSH. This will print out a banner before a user gets prompted for a password. After a user logs into VyOS, if the post-login is set, VyOS will print out the banner once the user logs in. This concludes the commands used for securing VyOS.

pfSense

The same can be done for pfSense. Open the web browser, point your browser to pfSense (in my case, http://10.249.2.1), and login to your pfSense web interface. Once you get to the main interface, follow instructions as follows.

  1. In the System menu, open the User Manager.
  2. Click in the + Add button below the list of users.
  3. Enter the Username, Password, and Full Name. No spaces in the username.
  4. In the Group Membership area, select admins and click in Move to “member of” list. This will move the admins group to the “member of” list.
  5. Save the changes, log out, and log back in as the new admin user you have created in step 4.
  6. In the user manager, click in the pencil icon (Edit) to edit the admin user.
  7. Check the checkbox for Disabled. An admin user cannot login once the checkbox is selected.
  8. When done, Save the changes.

Try to login as admin. If successful, you should not be able to log in as an admin user but instead log in as a new user. This concludes the step-by-step instructions for pfSense.

Conclusion

Preventing a root or admin user from logging into a router is one of the security’s best practices. You can help ensure that bots and miscreants won’t be able to gain access to your router without the correct username and password. Even when bots are performing a brute-force attack. Still, it’s important to restrict access to the router through the use of a management subnet and if using pfSense, setup a root and server certificate in the Cert. Manager within the System menu and add a root certificate to your web browser of your choice. Use a management subnet for any devices that have SSH access or a web interface and do not allow managers, sales, web developers, or any other non-IT departments access to the critical network infrastructure.

Update: I just hit “c” twice in my keyboard (ccode instead of code) even though I only typed “c” just once. Ugh… Maybe I just need a different keyboard that prevents double-types regardless of the operating system I’m using… (And yes, I’m using Arch Linux.)

IPv4 Subnetting Practice

If you understand computer networking and know how IPv4 subnetting works, here’s a zip file which contains a self-contained HTML file. Double-click in the HTML file and you can begin practicing.

ipv4subnet.zip

Have fun!