Adjust Screen Resolution for Raspberry Pi

Open config.txt
sudo nano /boot/config.txt

After #hdmi_force_hotplug=1 enter the following line:
hdmi_cvt=1920 1080 60 3 0 0 0

width(required)width in pixels
height(required)height in pixels
framerate(required)framerate in Hz
aspect3aspect ratio 1=4:3, 2=14:9, 3=16:9, 4=5:4, 5=16:10, 6=15:9
margins00=margins disabled, 1=margins enabled
interlace00=progressive, 1=interlaced
rb00=normal, 1=reduced blanking
Key table values for above _cvt adjustment.

Uncomment and modify the following values appropriately:


hdmi_group=2 means DMT (Display Monitor Timing).
hdmi_mode=87 indicated the resolution mode set previously.
hdmi_drive=2 selects the Normal HDMI mode.

Resetting ESXi Evaluation License

## remove license
echo 'Removing License'
rm -r /etc/vmware/license.cfg
## get a new trial license
echo 'Copying new license'
cp /etc/vmware/.#license.cfg /etc/vmware/license.cfg
## restart services
echo 'Restarting VPXA'
/etc/init.d/vpxa restart

Edit /etc/rc.local.d/ and add:

/bin/kill $(cat /var/run/
/bin/echo "**59** /vmfs/volumes/ZFS0/" >> /var/spool/cron/crontabs/root

This will reset the license via cron job every 59 days (eval license expires 60 days).

This was confirmed working in ESXI v7.0 U1.

Install Organizr on Ubuntu 20.04

I couldn’t find a good and concise setup guide for Organizr on Ubuntu – everything wanted me to use Docker which, admittedly, I haven’t started learning well enough to setup my lab management console on.

One thing I learned from this process – running your webserver on NGINX instead of, say, Apache is known as a LEMP stack. I’d only ever heard of LAMP or WAMP previously. Now you know, too.

So first things first, setup a LEMP stack.

Linux is taken care of by Ubuntu 20.04, duh. Make sure it’s fully updated and freshly rebooted for good measure. Also make sure OpenSSH is installed if that’s not part of your typical Ubuntu install regimen.

Installing NGINX

sudo apt install -y nginx
sudo ufw status
sudo ufw enable (if not already enabled)
sudo ufw app list (to find out what options exist for ufw profiles)
sudo ufw allow OpenSSH (if not already allowed)
sudo ufw allow 'Nginx Full' (this will allow both HTTP and HTTPS) 

Now, navigating to should produce the screen below. If you're not seeing this screen, check your firewall NAT rules and be sure 80 is allowed inbound. 

Installing MySQL

sudo apt install mysql-server
sudo mysql_secure_installation
Now this is going to trigger an SAT-like questionnaire that you need to go through to make sure your MySQL setup is as secure as possible. It's all pretty self-explanatory. Just read the daggum prompts and you'll be fine. Hint: It's pretty much "Y" all the way through. 

Installing PHP

Just like that, we’re at the end of setting up our LEMP stack.

sudo apt install php-fpm php-mysql php-curl php-zip php-simplexml php-sqlite3

The last 4 of those were "gotchas" from the actual Organizr install. They're pre-requisites that aren't picked up during any other install. You can add them here and now or you can wait until you get the error and install them later. Biggest takeaway is - whatever the error is you see, add php- to the beginning for the install and you'll be fine. 

The rest of the setup and testing is all documented well enough by Digital Ocean here.

Securing the site with Let’s Encrypt

Using Let’s Encrypt makes having that spiffy HTTPS lock icon a breeze. The steps are all beautifully documented by Digital Ocean, once again, here.

Organizr Install

Now it’s time for the actual install of Organizr! And trust me, the hard work is already done!

cd /var/www/html/thisnerdyguy where thisnerdyguy is the name of your Organizr site. 
git clone /var/www/html/thisnerdyguy
chown -R www-data:www-data /var/www/html/

Lastly, edit your sites-available config for your Organizr site and be sure it resembles the following:

    root /var/www/html/thisnerdyguy;
    index index.php index.html index.htm index.nginx-debian.html;
    location / { try_files $uri $uri/ =404; }
    location ~ \.php$ {
        include snippets/fastcgi-php.conf;
        fastcgi_pass unix:/run/php/php7.4-fpm.sock;
    location /api/v2 {
	    try_files $uri /api/v2/index.php$is_args$args;


You should be all set! Navigating to should, depending on how you configured Let’s Encrypt, redirect to and, equally important, should load Organizr asking you to begin the very simple configuration process.


LTE WAN Failover with Sophos XG

Living in the land of moose and trees, power outages are a way of life. Either you’re fortunate enough to have an automatic standby generator, like a Generac, or you have a portable generator with the beloved “widowmaker” cobbled extension cord.

The problem with power outages here as opposed to somewhere like Texas is the outages up here are often due to trees coming down on the lines – which also takes down cable internet.

Of course we can tether our mobile phones to our laptops but that doesn’t do anything for the RokuTV, aka “The Babysitter”. And anyone who has let a 3 year old use their laptop understands why using my Macbook Pro 2013 nor my wife’s i7 Asus Zenbook are not options. We could also just let her use her Amazon Fire tablet, aka “The Other Babysitter”, but that’s just too easy, ok!?

On our Generac I am running a DIY-project that monitors and controls the outage and exercise cycles. While the exercise cycles are of little importance, I do want to know if the power goes out while I’m away from the house. The notification is controlled by a small box that is roughly 50 feet away from the house so no amount of tethering will work there.

Running a Sophos XG135w gives me a few options. If this were the XG135, and not the -w version with integrated wireless, I could get an LTE add-on card that just needs a SIM card and some minor setup. Alas, because I have built-in wireless, an all-one-box is not an option. Truthfully, even if it were an option, those add-on cards are very expensive.

Apparently prices have gone up…I blame COVID…they used to be around $80 but now around $100, the Netgear LB1120 is an LTE modem that outputs via ethernet as opposed to many of the other modem options that are basically fancy hotspots.

So you put your activated SIM card into the LB1120. I’ve used this model with Google Fi which runs on US Cellular, T-Mobile, and Sprint. We don’t have Sprint up here in the North Pole but USC and TMo are decent enough. I’m now running it with a VZW SIM and holy cow! The signal strength is so much better. I was getting about 2 bars with any carrier on Google Fi and I now get 4 bars on VZW.

Once the activated SIM card is inserted, plug in the power and plug in the ethernet cable to your computer. Power on the device. Navigate to (default) and login using the password printed on the bottom of the modem (varies by device).

The only required change is to set the LB1120 as Bridge as Router is default. If you’re running Google Fi, you’ll need to set the APN to h2g2. Next time I’m doing maintenance, I’ll take screenshots since I need to either remove the modem from the network or let a failover occur.

Once your changes are made, shutdown the LB1120. Plug in an ethernet cable from the LB1120 to a port on the XG135w. I used Port 8 for no real reason. Port 1 is screen-printed LAN, Port 2 WAN and Port 3 DMZ. Since these are only screen-prints and we can configure these ports however we want, it’s really only my OCD that keeps me from using Port 3. So yes, Port 8. Could I have used Port 4? Sure. But it’s Port 8.

Port 1 – LAN
Port 2 – WAN
Port 3 – DMZ
Port 8 – LTE

Ok, so once you have the physical connections made, login to the XG., default.

Network> Interfaces> Select the port that is connected to the LB1120. In my case, Port 8.


I changed the name to LTE, again, OCD. Name the interface whatever you want.
Network Zone: WAN
IP assignment: DHCP
Gateway Name: DHCP_LTE_GW Name this whatever you want. I chose to keep with the default naming convention for the gateway.

Network> WAN Link Manager> Because we selected WAN as the Network Zone previously, we will notice the new Gateway shown.

Click on the new IPv4 Gateway. Mine is named DHCP_LTE_GW.

Set the Interface Type. This will be a Backup connection so I will unsurprisingly select “Backup”

Set the Interface Details according to your needs. The verbiage is nice and simple so you can tune this to your usage.
Activate this Gateway: If DHCP_Port2_GW fails.
Action on Activation: Inherit the weight of failed active gateway.
Action on Fallback: Serve all connections through restored gateway.

I left the Failover Rules at default.

That should do it. I tested my setup by unplugging my cable modem and it did, indeed, fail over appropriately. It happened fast enough that Netflix didn’t buffer and my beloved, possessed offspring didn’t even notice! The speedtest showed 14Mbps which isn’t amazing by cable standards but when you consider this is Verizon going through trees, to my cellular booster on my metal roof, to and out my indoor dome cellular booster antenna, and into my LTE modem in my basement.

How to update ESXi 7.0 to 7.0 U1b

Why would anyone want to update to a minor revision from a perfectly working version? All versions prior to this minor revision required some every boot “hacking” of the ethernet driver if you also want to run a USB NIC, which is very handy when you’re using a NIC and want a second interface.

Enable SSH.

Enter Maintenance Mode.

esxcli software profile update -p ESXi-7.0U1b-17168206-standard -d


How to remove derelict or broken VMs from ESXi via CLI

I recently ran into an issue with one of my SSDs failing in my ESXi v6.7 NUC. All of my client VMs were house on this, now dead, datastore so all VMs were showing in vSphere Web as “Invalid.” One of the unfortunate side-effects of this error is the complete inability to really do anything to the VM – you can’t edit settings, you can’t unregister, and you definitely can’t delete.

Luckily, it is possible to delete these abominations. Unluckily, it requires some command-line magic.

The first step is to enable SSH on the ESXi host. No reboot is needed to enable SSH.

Once SSH is enabled, access the ESXi host and login.

vim-cmd /vmsvc/getallvms

Will list all VMs either currently or previously registered to this particular ESXi instance. I don’t remember the exact verbiage but it reported something to the effect of:

Skipping invalid VM 1

This error repeated for each VM that had been taken along with the now defunct SSD. Once the list of failed VMs is shown, run the following command for each VM:

vim-cmd /vmsvc/unregister <id>

And that’s it. All ghosts of the former VMs should be removed and your ESXi WebUI should be clear of errored clients.

Caldera Setup

Clone the Repository

git clone --recursive --branch 2.8.1

Change directories into the “CALDERA” directory

cd caldera

Install PIP and the PIP requirements:

sudo apt install -y python3-pip
pip3 install -r requirements.txt

Install Go

Installing Go is technically optional but it makes it so that agent executables are dynamically compiled and they avoid AV detection much better.

Download Go from

Extract the downloaded file (your filename may vary)

sudo tar -C /usr/local -xzf go1.15.2.linux-amd64.tar.gz

Update your PATH

Add the following line to your $HOME/.bashrc file:

export PATH=$PATH:/usr/local/go/bin

Close the terminal and reopen to have the PATH changes take effect, or use the “source ~/.bashrc” command.

Confirm that GO is properly installed by checking its version.

go --version

Start the server

Create a copy of the CALDERA config file called local.yml and then edit it to set your own users and secure passwords.

cp ~/caldera/conf/default.yml ~/caldera/conf/local.yml

Edit the local.yml file to change the usernames and passwords shown below to something more secure.

Start the CALDERA server.


( Not functional below)

Setup SSL Communications for the CALDERA Web Interface

If your CALDERA web interface is reachable over an untrusted network, you should enable encrypted communications as instructed below.

The encrypted communications are handled by the HAProxy tool. Install HAProxy as follows.

sudo apt update
sudo apt install haproxy

After logging in to the CALDERA web interface on localhost:8888, go to the Advanced–>Configuration menu.

From the configuration menu enable the SSL plugin. You can now reach the CALDERA web interface at https://<your ip>:8443.

You also need to update your setting from the CALDERA web interface (advanced–>configuration) to include https as shown below. (update with the IP or domain name of your server)

Note: Make sure you do not include a trailing slash (/) on the URL.

Don’t forget to click the green “update” button and restart the server after making the configuration changes.

Now that we have configured the setting, we will see updated commands for deploying an agent using the http contact method (54ndc47 for example)

By default, a self signed certificate is used for the SSL encryption. Replace the self-signed certificate at ~/caldera/plugins/ssl/conf/insecure_certificate.pem with your own if desired.

Need to create your own signed/trusted certificate? Try using Let’s Encrypt. You will need to own a domain name and configure a DNS authoritative record to point to your CALDERA server’s IP address.

To use your own trusted cert create a combined pem file using the commands to below.

cd /etc/letsencrypt/live/<your domain>
cat cert.pem privkey.pem > ~/caldera/plugins/ssl/conf/insecure_certificate.pem

Restart the CALDERA server after making these changes.

If you use the self-signed cert, any PowerShell commands you run to get a remote agent are going to complain about not being able to establish a trust relationship. You will need to bypass the trust check by running the PowerShell commands below before you execute the agent command. (this only applies if you are using the default self-signed cert)

class TrustAllCertsPolicy : System.Net.ICertificatePolicy {
        [bool] CheckValidationResult([System.Net.ServicePoint] $a,
            [System.Security.Cryptography.X509Certificates.X509Certificate] $b,
            [System.Net.WebRequest] $c,
            [int] $d) {
            return $true
    [System.Net.ServicePointManager]::CertificatePolicy = [TrustAllCertsPolicy]::new()

Now your CALDERA SERVER is fully set up and ready to be put to use. Check out the “Attack Emulation: Atomic Red Team, CALDERA, and More” class to learn more about using Mitre CALDERA, including over 25 hands-on labs.

Managing Sophos XG – Create VLANs

Pre-setup disclaimers:
– Some steps will vary depending on exact deployment. For this particular lab, a single trunk port is used between the XG Port 2 and the Switch (SW) Port 1. To maximize network throughput, this setup could split trunk ports between multiple 1Gbe ports on the XG to multiple 1Gbe ports on the SW. But that’s a project for another day.
– The switch used is a now-deprecated Linksys switch, SGE-2000P. It’s a faithful old steed and provides good PoE to the devices still tolerant of -at standards in an -af world.

Create the Interfaces

The first step is to create the interfaces needed for each VLAN. You’ll need to know which port(s) you’re trunking from the XG.

XG> Configure> Network> Interfaces> Add New Interface> Add VLAN>
– Give it an appropriate name.
– Select the appropriate trunk port.
– Select the desired zone. For the scope of this walkthrough, all VLANs will be “LAN”.
– Set the desired VLAN ID. It’s personal preference but I set the VLAN ID to the IP range of
that VLAN. It helps keeps my mind straight.
– Set your static IP range applicable to the VLAN. IE: for the VLAN 160.
– Save the changes.

Setup DHCP

Each VLAN will need it’s own DHCP range to work properly.

XG> Configure> Network> DHCP> Server> Add>
– Give it a descriptive name. I tend to keep everything named with a convention similar to the VLAN itself.
– Select the interface for the VLAN.
– Set the dynamic IP lease range. I will typically set this smaller than the total to leave me some room for static assignments. IE: –
– Set your static IP maps, if applicable. This is easy enough to do later, too.
– Leave “Use Interface IP as Gateway” checked.
– Leave all else default and Save changes.

XG Setup Complete!

That really is it – in the simplest sense. The default firewall rule will allow all traffic out so, unless otherwise specified, every VLAN should provide an IP via the DHCP server specified for that VLAN, provide DNS via the gateway IP for that VLAN, and allow all outbound.

Of course, this is useless without a properly configured switch. This is where things get tricky because unless you’re on the Cisco train, everything is done just a bit differently across all vendors. The next set of steps will apply to my switch as more of a “so I don’t forget” set of instructions to my future, post-annual-lab-nuke self but the overall concepts and lingo will hopefully help others conceptualize what they need to do in their labs.

Bring on the Switching

We first need to tell the switch what VLANs to expect from the XG.
VLAN Management> Properties> Add:
– Set the VLAN ID to a value set within the XG VLAN Interface.
– Give it a descriptive name.
– Do this for each VLAN set in the XG.

We then need to set the SW ports to the appropriate VLAN Mode.
VLAN Management> Interface Settings:
– Set Port 1 to Trunk (Access to XG)

Almost lastly, we need to tell the SW which VLANs to expect over the trunk Port 1.
VLAN Management> VLAN to Port> g1> Join VLAN:
– There will be one untagged VLAN and the rest tagged.
– The untagged VLAN will be the default VLAN assigned to non-VLAN-aware devices. This can be the admin VLAN (not recommended) or a guest VLAN or anything you choose.
– The tagged VLANs will be the remaining VLANs available to be assigned elsewhere in the switch. So we can tag VLAN 100 to Port 8 or VLAN 200 to Port 16.

I still need to go over the difference between “General” and “Access” ports as they relate to my schema but that’ll be an update for another night. As will screenshots.

How To Install WordPress with LAMP on Ubuntu 18.04


WordPress is the most popular CMS (content management system) on the internet. It allows you to easily set up flexible blogs and websites on top of a MySQL backend with PHP processing. WordPress has seen incredible adoption and is a great choice for getting a website up and running quickly. After setup, almost all administration can be done through the web frontend.

In this guide, we’ll focus on getting a WordPress instance set up on a LAMP stack (Linux, Apache, MySQL, and PHP) on an Ubuntu 18.04 server.


In order to complete this tutorial, you will need access to an Ubuntu 18.04 server.

You will need to perform the following tasks before you can start this guide:

  • Create a sudo user on your server: We will be completing the steps in this guide using a non-root user with sudo privileges. You can create a user with sudo privileges by following our Ubuntu 18.04 initial server setup guide.
  • Install a LAMP stack: WordPress will need a web server, a database, and PHP in order to correctly function. Setting up a LAMP stack (Linux, Apache, MySQL, and PHP) fulfills all of these requirements. Follow this guide to install and configure this software.
  • Secure your site with SSL: WordPress serves dynamic content and handles user authentication and authorization. TLS/SSL is the technology that allows you to encrypt the traffic from your site so that your connection is secure. The way you set up SSL will depend on whether you have a domain name for your site.
    • If you have a domain name… the easiest way to secure your site is with Let’s Encrypt, which provides free, trusted certificates. Follow our Let’s Encrypt guide for Apache to set this up.
    • If you do not have a domain… and you are just using this configuration for testing or personal use, you can use a self-signed certificate instead. This provides the same type of encryption, but without the domain validation. Follow our self-signed SSL guide for Apache to get set up.

When you are finished with the setup steps, log into your server as your sudo user and continue below.

Step 1 – Creating a MySQL Database and User for WordPress

The first step that we will take is a preparatory one. WordPress uses MySQL to manage and store site and user information. We have MySQL installed already, but we need to make a database and a user for WordPress to use.

To get started, log into the MySQL root (administrative) account by issuing this command:

mysql -u root -p

You will be prompted for the password you set for the MySQL root account when you installed the software.

First, we can create a separate database that WordPress will control. You can call this whatever you would like, but we will be using wordpress in this guide to keep it simple. Create the database for WordPress by typing:


Note: Every MySQL statement must end in a semi-colon (;). Check to make sure this is present if you are running into any issues.

Next, we are going to create a separate MySQL user account that we will use exclusively to operate on our new database. Creating one-function databases and accounts is a good idea from a management and security standpoint. We will use the name wordpressuser in this guide. Feel free to change this if you’d like.

We are going to create this account, set a password, and grant access to the database we created. We can do this by typing the following command. Remember to choose a strong password here for your database user:

GRANT ALL ON wordpress.* TO 'wordpressuser'@'localhost' IDENTIFIED BY 'password';

You now have a database and user account, each made specifically for WordPress. We need to flush the privileges so that the current instance of MySQL knows about the recent changes we’ve made:


Exit out of MySQL by typing:


Step 2 – Installing Additional PHP Extensions

When setting up our LAMP stack, we only required a very minimal set of extensions in order to get PHP to communicate with MySQL. WordPress and many of its plugins leverage additional PHP extensions.

We can download and install some of the most popular PHP extensions for use with WordPress by typing:

sudo apt update
sudo apt install php-curl php-gd php-mbstring php-xml php-xmlrpc php-soap php-intl php-zip

Note: Each WordPress plugin has its own set of requirements. Some may require additional PHP packages to be installed. Check your plugin documentation to discover its PHP requirements. If they are available, they can be installed with apt as demonstrated above.

We will restart Apache to load these new extensions in the next section. If you are returning here to install additional plugins, you can restart Apache now by typing:

sudo systemctl restart apache2

Step 3 – Adjusting Apache’s Configuration to Allow for .htaccess Overrides and Rewrites

Next, we will be making a few minor adjustments to our Apache configuration. Based on the prerequisite tutorials, you should have a configuration file for your site in the /etc/apache2/sites-available/ directory. We’ll use /etc/apache2/sites-available/wordpress.conf as an example here, but you should substitute the path to your configuration file where appropriate.

Additionally, we will use /var/www/wordpress as the root directory of our WordPress install. You should use the web root specified in your own configuration.

Note: It’s possible you are using the 000-default.conf default configuration (with /var/www/html as your web root). This is fine to use if you’re only going to host one website on this server. If not, it’s best to split the necessary configuration into logical chunks, one file per site.

Enabling .htaccess Overrides

Currently, the use of .htaccess files is disabled. WordPress and many WordPress plugins use these files extensively for in-directory tweaks to the web server’s behavior.

Open the Apache configuration file for your website:

sudo nano /etc/apache2/sites-available/wordpress.conf

To allow .htaccess files, we need to set the AllowOverride directive within a Directory block pointing to our document root. Add the following block of text inside the VirtualHost block in your configuration file, being sure to use the correct web root directory: /etc/apache2/sites-available/wordpress.conf

<Directory /var/www/wordpress/>
    AllowOverride All

When you are finished, save and close the file.

Enabling the Rewrite Module

Next, we can enable mod_rewrite so that we can utilize the WordPress permalink feature:

sudo a2enmod rewrite

Enabling the Changes

Before we implement the changes we’ve made, check to make sure we haven’t made any syntax errors:

sudo apache2ctl configtest

The output might have a message that looks like this:

OutputAH00558: apache2: Could not reliably determine the server's fully qualified domain name, using Set the 'ServerName' directive globally to suppress this message
Syntax OK

If you wish to suppress the top line, just add a ServerName directive to your main (global) Apache configuration file at /etc/apache2/apache2.conf. The ServerName can be your server’s domain or IP address. This is just a message however and doesn’t affect the functionality of our site. As long as the output contains Syntax OK, you are ready to continue.

Restart Apache to implement the changes:

sudo systemctl restart apache2

Next, we will download and set up WordPress itself.

Step 4 – Downloading WordPress

Now that our server software is configured, we can download and set up WordPress. For security reasons in particular, it is always recommended to get the latest version of WordPress from their site.

Change into a writable directory and then download the compressed release by typing:

cd /tmp
curl -O

Extract the compressed file to create the WordPress directory structure:

tar xzvf latest.tar.gz

We will be moving these files into our document root momentarily. Before we do, we can add a dummy .htaccess file so that this will be available for WordPress to use later.

Create the file by typing:

touch /tmp/wordpress/.htaccess

We’ll also copy over the sample configuration file to the filename that WordPress actually reads:

cp /tmp/wordpress/wp-config-sample.php /tmp/wordpress/wp-config.php

We can also create the upgrade directory, so that WordPress won’t run into permissions issues when trying to do this on its own following an update to its software:

mkdir /tmp/wordpress/wp-content/upgrade

Now, we can copy the entire contents of the directory into our document root. We are using a dot at the end of our source directory to indicate that everything within the directory should be copied, including hidden files (like the .htaccess file we created):

sudo cp -a /tmp/wordpress/. /var/www/wordpress

Step 5 – Configuring the WordPress Directory

Before we do the web-based WordPress setup, we need to adjust some items in our WordPress directory.

Adjusting the Ownership and Permissions

One of the big things we need to accomplish is setting up reasonable file permissions and ownership.

We’ll start by giving ownership of all the files to the www-data user and group. This is the user that the Apache webserver runs as, and Apache will need to be able to read and write WordPress files in order to serve the website and perform automatic updates.

Update the ownership with chown:

sudo chown -R www-data:www-data /var/www/wordpress

Next we will run two find commands to set the correct permissions on the WordPress directories and files:

sudo find /var/www/wordpress/ -type d -exec chmod 750 {} \;
sudo find /var/www/wordpress/ -type f -exec chmod 640 {} \;

These should be a reasonable permissions set to start with. Some plugins and procedures might require additional tweaks.

Setting up the WordPress Configuration File

Now, we need to make some changes to the main WordPress configuration file.

When we open the file, our first order of business will be to adjust some secret keys to provide some security for our installation. WordPress provides a secure generator for these values so that you do not have to try to come up with good values on your own. These are only used internally, so it won’t hurt usability to have complex, secure values here.

To grab secure values from the WordPress secret key generator, type:

curl -s

You will get back unique values that look something like this:

Warning! It is important that you request unique values each time. Do NOT copy the values shown below!

Outputdefine('AUTH_KEY',         '1jl/vqfs<XhdXoAPz9 DO NOT COPY THESE VALUES c_j{iwqD^<+c9.k<J@4H');
define('SECURE_AUTH_KEY',  'E2N-h2]Dcvp+aS/p7X DO NOT COPY THESE VALUES {Ka(f;rv?Pxf})CgLi-3');
define('LOGGED_IN_KEY',    'W(50,{W^,OPB%PB<JF DO NOT COPY THESE VALUES 2;y&,2m%3]R6DUth[;88');
define('NONCE_KEY',        'll,4UC)7ua+8<!4VM+ DO NOT COPY THESE VALUES #`DXF+[$atzM7 o^-C7g');
define('AUTH_SALT',        'koMrurzOA+|L_lG}kf DO NOT COPY THESE VALUES  07VC*Lj*lD&?3w!BT#-');
define('SECURE_AUTH_SALT', 'p32*p,]z%LZ+pAu:VY DO NOT COPY THESE VALUES C-?y+K0DK_+F|0h{!_xY');
define('LOGGED_IN_SALT',   'i^/G2W7!-1H2OQ+t$3 DO NOT COPY THESE VALUES t6**bRVFSD[Hi])-qS`|');
define('NONCE_SALT',       'Q6]U:K?j4L%Z]}h^q7 DO NOT COPY THESE VALUES 1% ^qUswWgn+6&xqHN&%');

These are configuration lines that we can paste directly in our configuration file to set secure keys. Copy the output you received now.

Now, open the WordPress configuration file:

sudo nano /var/www/wordpress/wp-config.php

Find the section that contains the dummy values for those settings. It will look something like this: /var/www/wordpress/wp-config.php

. . .

define('AUTH_KEY',         'put your unique phrase here');
define('SECURE_AUTH_KEY',  'put your unique phrase here');
define('LOGGED_IN_KEY',    'put your unique phrase here');
define('NONCE_KEY',        'put your unique phrase here');
define('AUTH_SALT',        'put your unique phrase here');
define('SECURE_AUTH_SALT', 'put your unique phrase here');
define('LOGGED_IN_SALT',   'put your unique phrase here');
define('NONCE_SALT',       'put your unique phrase here');

. . .

Delete those lines and paste in the values you copied from the command line: /var/www/wordpress/wp-config.php

. . .


. . .

Next, we need to modify some of the database connection settings at the beginning of the file. You need to adjust the database name, the database user, and the associated password that we configured within MySQL.

The other change we need to make is to set the method that WordPress should use to write to the filesystem. Since we’ve given the web server permission to write where it needs to, we can explicitly set the filesystem method to “direct”. Failure to set this with our current settings would result in WordPress prompting for FTP credentials when we perform some actions.

This setting can be added below the database connection settings, or anywhere else in the file: /var/www/wordpress/wp-config.php

. . .

define('DB_NAME', 'wordpress');

/** MySQL database username */
define('DB_USER', 'wordpressuser');

/** MySQL database password */
define('DB_PASSWORD', 'password');

. . .

define('FS_METHOD', 'direct');

Save and close the file when you are finished.

Step 6 – Completing the Installation Through the Web Interface

Now that the server configuration is complete, we can complete the installation through the web interface.

In your web browser, navigate to your server’s domain name or public IP address:


Select the language you would like to use.

Next, you will come to the main setup page.

Select a name for your WordPress site and choose a username (it is recommended not to choose something like “admin” for security purposes). A strong password is generated automatically. Save this password or select an alternative strong password.

Enter your email address and select whether you want to discourage search engines from indexing your site:

When you click ahead, you will be taken to a page that prompts you to log in:

Once you log in, you will be taken to the WordPress administration dashboard:


WordPress should be installed and ready to use! Some common next steps are to choose the permalinks setting for your posts (can be found in Settings > Permalinks) or to select a new theme (in Appearance > Themes). If this is your first time using WordPress, explore the interface a bit to get acquainted with your new CMS.

How To Secure Apache with Let’s Encrypt on Ubuntu 18.04

(Verified fully functional 11/2020)


Let’s Encrypt is a Certificate Authority (CA) that provides an easy way to obtain and install free TLS/SSL certificates, thereby enabling encrypted HTTPS on web servers. It simplifies the process by providing a software client, Certbot, that attempts to automate most (if not all) of the required steps. Currently, the entire process of obtaining and installing a certificate is fully automated on both Apache and Nginx.

In this tutorial, you will use Certbot to obtain a free SSL certificate for Apache on Ubuntu 18.04 and set up your certificate to renew automatically.

This tutorial will use a separate Apache virtual host file instead of the default configuration file. We recommend creating new Apache virtual host files for each domain because it helps to avoid common mistakes and maintains the default files as a fallback configuration.


To follow this tutorial, you will need:

  • One Ubuntu 18.04 server set up by following this initial server setup for Ubuntu 18.04 tutorial, including a sudo non-root user and a firewall.
  • A fully registered domain name. This tutorial will use your_domain as an example throughout. You can purchase a domain name on Namecheap.
  • Both of the following DNS records set up for your server.
    • An A record with your_domain pointing to your server’s public IP address.
    • An A record with www.your_domain pointing to your server’s public IP address.
  • Apache installed by following How To Install Apache on Ubuntu 18.04. Be sure that you have a virtual host file for your domain. This tutorial will use /etc/apache2/sites-available/your_domain.conf as an example.

Step 1 — Installing Certbot

The first step to using Let’s Encrypt to obtain an SSL certificate is to install the Certbot software on your server.

Certbot is in very active development, so the Certbot packages provided by Ubuntu tend to be outdated. However, the Certbot developers maintain a Ubuntu software repository with up-to-date versions, so we’ll use that repository instead.

First, add the repository:

sudo add-apt-repository ppa:certbot/certbot

You’ll need to press ENTER to accept.

Install Certbot’s Apache package with apt:

sudo apt install python-certbot-apache

Certbot is now ready to use, but in order for it to configure SSL for Apache, we need to verify some of Apache’s configuration.

Step 2 — Set Up the SSL Certificate

Certbot needs to be able to find the correct virtual host in your Apache configuration for it to automatically configure SSL. Specifically, it does this by looking for a ServerName directive that matches the domain you request a certificate for.

If you followed the virtual host set up step in the Apache installation tutorial, you should have a VirtualHost block for your domain at /etc/apache2/sites-available/ with the ServerName directive already set appropriately.

To check, open the virtual host file for your domain using nano or your favorite text editor:

sudo nano /etc/apache2/sites-available/your_domain.conf

Find the existing ServerName line. It should look like this: /etc/apache2/sites-available/your_domain.conf

ServerName your_domain;

If it does, exit your editor and move on to the next step.

If it doesn’t, update it to match. Then save the file, quit your editor, and verify the syntax of your configuration edits:

sudo apache2ctl configtest

If you get an error, reopen the virtual host file and check for any typos or missing characters. Once your configuration file’s syntax is correct, reload Apache to load the new configuration:

sudo systemctl reload apache2

Certbot can now find the correct VirtualHost block and update it.

Next, let’s update the firewall to allow HTTPS traffic.

Step 3 — Allowing HTTPS Through the Firewall

If you have the ufw firewall enabled, as recommended by the prerequisite guides, you’ll need to adjust the settings to allow for HTTPS traffic. Luckily, Apache registers a few profiles with ufw upon installation.

You can see the current setting by typing:

sudo ufw status

It will probably look like this, meaning that only HTTP traffic is allowed to the web server:

OutputStatus: active

To                         Action      From
--                         ------      ----
OpenSSH                    ALLOW       Anywhere                  
Apache                     ALLOW       Anywhere                  
OpenSSH (v6)               ALLOW       Anywhere (v6)             
Apache (v6)                ALLOW       Anywhere (v6)

To additionally let in HTTPS traffic, allow the Apache Full profile and delete the redundant Apache profile allowance:

sudo ufw allow 'Apache Full'
sudo ufw delete allow 'Apache'

Your status should now look like this:

sudo ufw status
OutputStatus: active

To                         Action      From
--                         ------      ----
OpenSSH                    ALLOW       Anywhere                  
Apache Full                ALLOW       Anywhere                  
OpenSSH (v6)               ALLOW       Anywhere (v6)             
Apache Full (v6)           ALLOW       Anywhere (v6)        

Next, let’s run Certbot and fetch our certificates.

Step 4 — Obtaining an SSL Certificate

Certbot provides a variety of ways to obtain SSL certificates through plugins. The Apache plugin will take care of reconfiguring Apache and reloading the config whenever necessary. To use this plugin, type the following:

sudo certbot --apache -d your_domain -d www.your_domain

This runs certbot with the --apache plugin, using -d to specify the names you’d like the certificate to be valid for.

If this is your first time running certbot, you will be prompted to enter an email address and agree to the terms of service. After doing so, certbot will communicate with the Let’s Encrypt server, then run a challenge to verify that you control the domain you’re requesting a certificate for.

If that’s successful, certbot will ask how you’d like to configure your HTTPS settings:

OutputPlease choose whether or not to redirect HTTP traffic to HTTPS, removing HTTP access.
1: No redirect - Make no further changes to the webserver configuration.
2: Redirect - Make all requests redirect to secure HTTPS access. Choose this for
new sites, or if you're confident your site works on HTTPS. You can undo this
change by editing your web server's configuration.
Select the appropriate number [1-2] then [enter] (press 'c' to cancel):

Select your choice then hit ENTER. The configuration will be updated, and Apache will reload to pick up the new settings. certbot will wrap up with a message telling you the process was successful and where your certificates are stored:

 - Congratulations! Your certificate and chain have been saved at:
   Your key file has been saved at:
   Your cert will expire on 2018-07-23. To obtain a new or tweaked
   version of this certificate in the future, simply run certbot again
   with the "certonly" option. To non-interactively renew *all* of
   your certificates, run "certbot renew"
 - Your account credentials have been saved in your Certbot
   configuration directory at /etc/letsencrypt. You should make a
   secure backup of this folder now. This configuration directory will
   also contain certificates and private keys obtained by Certbot so
   making regular backups of this folder is ideal.
 - If you like Certbot, please consider supporting our work by:

   Donating to ISRG / Let's Encrypt:
   Donating to EFF:          

Your certificates are downloaded, installed, and loaded. Try reloading your website using https:// and notice your browser’s security indicator. It should indicate that the site is properly secured, usually with a green lock icon. If you test your server using the SSL Labs Server Test, it will get an A grade.

Let’s finish by testing the renewal process.

Step 5 — Verifying Certbot Auto-Renewal

The certbot package we installed takes care of renewals by including a renew script to /etc/cron.d, which is managed by a systemctl service called certbot.timer. This script runs twice a day and will automatically renew any certificate that’s within thirty days of expiration.

To check the status of this service and make sure it’s active and running, you can use:

sudo systemctl status certbot.timer

You’ll get output similar to this:

Output● certbot.timer - Run certbot twice daily
     Loaded: loaded (/lib/systemd/system/certbot.timer; enabled; vendor preset: enabled)
     Active: active (waiting) since Tue 2020-04-28 17:57:48 UTC; 17h ago
    Trigger: Wed 2020-04-29 23:50:31 UTC; 12h left
   Triggers: ● certbot.service

Apr 28 17:57:48 fine-turtle systemd[1]: Started Run certbot twice daily.

To test the renewal process, you can do a dry run with certbot:

sudo certbot renew --dry-run

If you see no errors, you’re all set. When necessary, Certbot will renew your certificates and reload Apache to pick up the changes. If the automated renewal process ever fails, Let’s Encrypt will send a message to the email you specified, warning you when your certificate is about to expire.


In this tutorial, you installed the Let’s Encrypt client certbot, downloaded SSL certificates for your domain, configured Apache to use these certificates, and set up automatic certificate renewal. If you have further questions about using Certbot, their documentation is a good place to start.