- Creating Bootable USB
- BIOS Tinkering
- Setting up Ubuntu
- Connecting to the Internet
- Cronjobs (Auto-Reconnect)
- SSH
- Auto-Update
- Hardening SSH
- Graphics Over SSH
- Power
- External Storage
- Website Setup
- Conclusion
Previously, I managed to set up this website on a Raspberry Pi SBC clone. While I was able to get the site running and accessible to the internet, the frequent power outages in my neighborhood as well as the fact that my family can mess with the Ethernet connection between the SBC and the router meant that self-hosting was not viable. I also ran out of time during winter break to think of a solution (I also found out that my site didn’t work on the flight back to Columbia, so it was way too late to fix anything). Hence, I ended up using Github Pages as a stopgap measure.
In the interim between the end of my internship at BNL and the start of the next year, I found my dad’s ten year old work laptop buried in one off our closets. He stopped using the thing because eventually, the windows 7 installation got corrupted and the battery died out. Since he had nothing of value on the device, I thought that if I replaced the battery and installed a brand new Linux distro that I could use it as a more reliable server.
The laptop solves both of the above problems that the SBC had:
- Built in UPS in the form of the battery, so the frequent brown-outs wouldn’t be totally crippling
- The laptop can be connected to both Wifi and Ethernet, so that if some one of my family members disconnects the Ethernet, I should still be good (as long as the router still works.)
What follows is what I did to set everything up. The reason for writing this is so that whe I inevitably have to do all of this again, I have a single reference to go to instead of a multitude of links.
Creating Bootable USB
In order to flash a new OS to the laptop, we want to create a bootable thumb drive. I choose to use Ubuntu Server as the distro, since that is the Linux flavor I am most familiar with. The most up to date version can be found here. Download the .iso file to the Desktop.
We now use RUFUS in order to create the bootable USB. Perform the following after downloading and opening RUFUS:
- Set path to .iso file in “Boot Selection” (make sure .iso is NOT on the USB)
- Rename Volume Label to whatever you want
- Change file system to NTFS for Windows compatibility
- Choose recommended options and say yes to all prompts
- Close RUFUS, Safely eject USB
BIOS Tinkering
We plug in the USB to the laptop, and power on. We spam either F2 or F8 in order to load the BIOS menu (which key you need to press is computer dependent).We make the following changes (Exact location of all of these is once again computer dependent):
- Enable Boot from USB
- Place USB boot at top of Boot Sequence
- Wake on AC
Don’t forget to Apply Changes.
Setting up Ubuntu
Upon booting up again, you should be greeted with the Ubuntu installation GUI. You can navigate the menus with TAB and ENTER. Do the following:
- Select Try or Install Ubuntu Server
- Select your language of choice
- Select “Use Default Mouse and Keyboard”
- Select Ubuntu Server
- Skip setting up internet connection (I wanted to do this via the command line for the learning experience. For simplicity, you should probably set up the connection now)
- Skip proxy
- Use default mirror
- Skip checking in with snap
- Use entire disk
- Confirm installation
At this point, pay special attention to the username, password, and server name you input.
- The user name and server name should be easy for you to remember
- The password should be easy for you to remember, but for better security, the password should be long
- It is more secure to have a longer password consisting of only letters and numbers than a shorter password consisting of the entire character set since the longer password has more possible combinations
- Pick a password that is at least 15 characters long and that you can remember easily
To finish installing:
- Skip Ubuntu Pro
- Install OpenSSH (Skip other options)
- Select Reboot Now
Logging In
- Once you see the prompt
<server_name>:login
in the output, you can now login. One annoying thing I encountered what that the boot messages would sometimes obscure the login prompt. So if you can’t see the above prompt the screen has stopped scrolling for a while, just spam ENTER until you can see the prompt.
- As the first command, type in
sudo usermod -aG sudo <user>
to give the user you set up admin privileges.
Connecting to the Internet
First thing, Plug in an Ethernet Cable to connect the computer to the router. I needed to buy a USB to Ethernet adapter since the laptop did not come with an Ethernet port.
Next, cd to /etc/netplan/ and remove all the .yaml files present. Normally, you would want to copy those config files in case you screw up a configuration, but these don’t do anything at the moment since you didn’t set up internet.
Run the following command to list all the network devices.
ls /sys/class/net
You should have ethernet (starts with e), a loopback device lo, and wifi (starts with w). Make a note of all the devices. For me, since I had the adapter, there was an additional connection enx00e04ce32d5c which I used instead of the other ethernet device.
Create a new .yaml file in /etc/netplan/ of the following form: 01-*.yaml. The 01 refers to the priority of the configuration.
We want to set up DHCP for both Ethernet and wireless, as well as assign the nameservers we want to use (Google’s nameservers are selected here.).
Format the config file as follows:
network:
version: 2
renderer: networkd
ethernets:
<ethernet_device>:
dhcp4: true
dhcp6: true
nameservers:
addresses:
- 8.8.8.8
- 8.8.4.4
wifis:
<Wifi_Device>:
dhcp4: true
dhcp6: true
access-points:
<NetworkSSID>:
password: "<Password>"
nameservers:
addresses:
- 8.8.8.8
- 8.8.4.4
where <Ethernet_device> and <Wifi_Device> are the name of the devices listed in ls /sys/class/net, and <NetworkSSID> and <Password> is the name of your wireless network and it’s the associated password. Don’t forget to wrap double quotes around the password to esacape any special characters you might have.
Pay special attention to ensure that you have the correct indentation because .yaml files are fickle like that.
Once you save file, run the command sudo netplan try <filename>. If there are no errors, then hit Enter. Otherwise, do Control C.
You should be connected to the internet at this point. If you want to turn on/off and interface, run: sudo ip link set <interface_name> down/up.
If you want to check the status of your connetion, type ip a.
In theory, I should probably specify a static private IP address in the config files, but what I ended up doing is having my router specify this instead.
Cronjobs (Auto-Reconnect)
Since the internet in my home is kind of flaky, I want to be able to reconnect to the wifi/ethernet whenever they come online again. Following this thread, we create a shell script to check if for internet connectivity. If no connectivity is found, we restart the wireless and ethernet connections.
Shell Script
Here is the shell script. Make the appropriate substitutions, then save the script as *.sh where * is whatever you wish. Afterwards, run chmod -X <script_name> to make the file executable. It would also be prudent to create a folder to store all your cronjobs that you might add in the future.
#!/bin/bash
message=`ping -c 2 google.com | grep -o "^[0-9]*" | head -1`
if [ $message="2" ]
then
ip link set <WifiDevice> up
ip link set <EternetDevice> up
fi
The script is pretty simple: ping -c 2 sends out two ping requests to whatever ip address you specify. If you get a response, then you get lines that start with 64 bytes indicating successful pings.
At the very end, regardless of the success status of the pings, you get some summary statistics. One line of the summary statistic will always read 2 packets transmitted.
What
message=`ping -c 2 google.com | grep -o "^[0-9]*" | head -1`
does is:
- send 2 ping requests to google.com
- grep each line and check for any number of digits at the beginning of each line
- use
head -1to get the first of these lines and store that in message
Then, if message equals “2”, we know that no successful pings were made, and we assume that wifi is down. Therefore, we run
ip link set <WifiDevice> up
ip link set <EternetDevice> up
to reestablish connection to each network device (remember, can find names of network devices via ls /sys/class/net).
Adding to Crontab
Run the following command:
sudo crontab -e
Then, select whatever editor you want.
Add the following line to the end of the file:
*/5 * * * * /bin/bash <abs path to script>
This will run the reconnection script every 5 minutes. You can change the update frequency as you desire.
To activate the script, you need to restart cron.
sudo systemctl restart cron
SSH
Now that the server has internet access, you should be able to SSH into the computer when connected on your LAN.
Running
hostname -I
will tell you the IP address assigned to the server. Take note of the IPv4 address.
Alternatively, you can run AngryIPScanner to find what IP you device is assigned to.
To ssh into the computer, run the following on another device on your LAN with command line access, and enter the password when prompted.
ssh <user>@<ip>
If you don’t want to display the system statistics on ssh, run
sudo nano /etc/pam.d/sshd
and edit out the pam_motd.so lines.
If you get an error like “Failed to connect to https://changelogs.ubuntu.com/meta-release-lts. Check your Internet connection or proxy settings”, run the following:
sudo apt reinstall ubuntu-release-upgrader-core
Afterwards, reboot the machine via
sudo reboot
Auto-Update
We first want to manually update our system. Run
sudo apt-get update && sudo apt-get dist-upgrade -y
To automate this process, we can install the following package:
sudo apt install unattended-upgrades
To configure, run the following command
dpkg-reconfigure --priority=low unattended-upgrades
Accept any prompts that pop up. Once done, system will auto update.
Hardening SSH
Before doing anything in this section, open up another terminal and ssh into the server. This is just in case you do something stupid and lock yourself out. You can use the second terminal to restore ssh configurations to default and try again.
Make sure that you run the following commands on only one terminal only.
cp /etc/ssh/sshd_config /etc/ssh/ssh_config_backup
sudo nano /etc/ssh/sshd_config
Make the following edits:
- PermitRootLogin no
- PasswordAuthentication no
- PermitEmptyPasswords no
- Port # (pick a port other than 22,80,443, 6000, 110, or any other ports that you aren’t going to use (see here for more info on what each port is meant for))
- ClientAliveInterval 0
- X11Forwarding yes (not for security purposes, but you need it for Graphics)
Enabling password-less Authentication
On whatever computer you want to be able to SSH from (denoted as client), run the following in order to set up your public and private keys:
cd ~/.ssh
ssh-keygen
Name your public private keys with with whatever you want. Run the following on both the public and private keys to only allow yourself read and write access to the keys.
chmod 600 *public_key*
chmod 600 *private_key*
Now copy over the PUBLIC key to your server:
ssh-copy-id -i <.pub file> <user>@<ip
You can make an alias on your client in your ~/.bashrc for each use
alias <short_command>='ssh -X <user>@<ip> -i ~/.ssh/<private_key> -p <port>'
Don’t forget to source ~/.bashrc to apply changes.
We need to restart ssh in order for this to take effect (NOTE: you may get kicked out of the server upon doing this). Run
sudo systemctl restart ssh
If you can’t log in, use the other terminal to restore to run the following commands to restore defaults:
cp /etc/ssh/ssh_config_backup /etc/ssh/sshd_config
sudo systemctl restart ssh
Fail2ban
Run the following commands to install and activate Fail2Ban:
sudo apt install fail2ban -y
sudo systemctl enable fail2ban
sudo systemctl start fail2ban
You can tinker around with Fail2ban settings by editing /etc/fail2ban/jail.conf. Just make sure that if you are going to do this, that you do the following:
- create a copy of the jail.conf file and edit the copy (changes made to default will get overridden on update)
cp /etc/fail2ban/jail.conf /etc/fail2ban/jail.local
- open up two terminals so that you don’t foolishly lock yourself out
Graphics Over SSH
If you made the change X11Forwarding yes during SSH and restarted ssh successfully, you have do all the configuration necessary. We need to download a program that displays graphics for testing.
Run the following to install imagemagick, which will let you display images.
sudo apt install imagemagick-6.q16 -y
Run hostname -I and take note of IPv4 address. On the client, run
ssh -X <user>@<ip>
Once logged in, type in display. You should see a wizard on the client computer.
Power
I want to be able to shove the server under a piece of furniture (not great for ventilation; good for deterring my family from messing with the computer). As such, I need the laptop to stay on even when the lid is closed.
Run sudo nano /etc/systemd/logind.conf, uncomment and change the following settings to “Ignore”:
- HandleLidSwitch
- HandleLidSwitchExternalPower
- HandleLidSwitchDocked
Afterwards, run sudo service systemd-logind restart to enact the settings.
External Storage
In addition to hosting this website, I also want to use the laptop as a backup of all my important files. Since SSH is already setup, I’ll be able to just scp the important directories over. We had an old 512 Gb external hard drive lying around, so I automounted it to the laptop. Here is how that was done:
After manually removing the important files on the drive, I had to reformat the drive. I choose to use NTFS as the filesystem since that is compatible across both Windows and Linux based systems (both of which I use on a daily basis).
I first plugged in the drive into the Server. Run
lsblk -f
This returns information about all the filesystems attached to the OS. Take note of what the name and location of the external drive is (can run lsblk -f before and after plugging in hard drive). Afterwards, run
sudo mkntfs /dev/<DriveName> -L <REPLACE_ME> -Q
where <DriveName> is what lsblk returned and <REPLACE_ME> is whatever you want to call your external hard drive. The -L flag sets the volume label of the device, while the -Q flag indicates a quick reformat.
Once the above is done, plug the device into a Windows computer and try to open via file explorer. If you cannot, re-run mkntfs on the server without the -Q option.
To automount the newly-reformatted hard drive, I followed this thread on AskUbuntu. To summarize:
mkdir <Folder>in your home directory on the server where<Folder>is whatever name you want- run
lsblk -fand take note of the UUID of the drive - Run
sudo nano /etc/fstaband place the following at the bottom of the file:
UUID=<ID> <external_drive_folder> ntfs defaults,nofail,x-systemd.device-timeout=30 0 0
- The various parameters:
<ID>is the UUID as shown bylsblk -f<external_drive_folder>is the absolute path to the folder you created
After saving the file, rebooting the server should cause the drive to automount.
Website Setup
Apache2
Run
sudo apt install apache2 -y
Ubuntu Server should already have it installed, but just in case.
You can check the status of apache with
sudo systemctl status apache2
and you can restart apache with
sudo systemctl restart apache2
You can view the default webpage by going to your another computer on your LAN and typing in the IP address of the server.
UFW
UFW should be installed by default.
Open another terminal, since you might lock yourself out.
Run the following commands to enable which ports the server can accept incoming connections on:
sudo ufw default deny incoming
sudo ufw default allow outgoing
sudo ufw allow <port>/tcp
sudo ufw allow "Apache Full"
sudo ufw enable
Replace <port> with the port you use for your ssh connection (defaults to 22).
Log out of one of your terminal instances and try to ssh in again. If you locked yourself out, use the other terminal instance to run the command sudo ufw reset to try again.
Github CLI
We need to install git and link to a github account to download some helpful scripts as well as provide a backup of our website. The bare minimum step are as follows:
- First, create a github account here.
- Copy and run the commands under Install
- You might need to skip commands involving
apt updatesince the auto-updater is running
- You might need to skip commands involving
- Create an SSH key (a guide can be found here)
- link your github CLI to your github account (found here under
Configuration)- Basically, run
gh auth loginand follow the prompts
- Basically, run
As something optional (but helpful), you can display the branch name of whatever repository you are in. Just follow this thread and modify your ~/.bashrc. Don’t forget to source your bashrc to enable the changes.
Static IP
I need a static private IP address so that the website doesn’t randomly go down whenever DHCP refreshes. I originally tried to set up the static IP address in the /etc/netplan/ config file. What I ended up doing instead was having my router assign the static IP there. Exact details vary depending on router.
Port Forwarding
Once again, the exact details are router dependent. The general gist is that you need to go to the Port Forwarding section of your router and expose Ports 80, 443, and Port X to enable Apache and SSH access (Replace X with whatever port you are using for SSH).
You can use this Port Checker to see if you have sucessfully forwarded your ports. If you can, you should be able to access the default apache webpage from any device by typing in your public ip addresss (search “What is my public IP address?” in Google).
Cloudflared
Instead of remembering an IP address, I wanted to be able to use a domain name. As such, you need to purchase a domain name. I chose to use Cloudflare; there are others domain name registrars that potentially are cheaper (like Namecheap or Freenom), but I had trouble with those. What follows is a rough outline of how to set everything up with Cloudflare.
To register a domain name, this guide explains how to set up a Cloudlflare account and register a domain.
Once that is done, you need to assign some DNS records to map your domain name to your public IP. Once you log into the Cloudflare dashboard, select your website, navigate to DNS, and add two records: and A and CNAME record.
The A record should map the domain name you purchased to your public IP address.
The CNAME record should add a subdomain to your domain (which I used for ssh access).
Once you verify that your domain name works in the web browser. you can assign a bash alias for SSHing from outside your LAN:
alias RemoteLogin='ssh -X <user>@<domain-name> -i <private_key> -p <port> -o ServerAliveInterval=600
where <user> is your admin username, <domain-name> is the domain name you registered, and <private_key> is location of your private key. <port> is only if you use a port other than 22.
In order for the above command to work, you need to edit an ssh config file following this thread. What you do is:
- run
sudo nano ~/.ssh.config - Edit the file to read as follows
Host <subdomain>.<domain>
Hostname <public_ip>
Port <port>
where <subdomain> is the subdomain you specified in the CNAME record, <domain> is your domain name, <public_ip> is you public ip address (can see that here) and <port> is the port SSH can access (defaults to 22 unless you changed it).
You can activate this by running sudo systemctl restart ssh.
Cloudflared DDNS
While the website might currently be up and running and accessible from the Internet, your public IP address is most likely not constant; your ISP provider periodically changes your public IP address. Luckily, someone has already solved this problem; this video by NetworkChuck explains the process, but to summarize, do the following on the server:
- run
git clone https://github.com/K0p1-Git/cloudflare-ddns-updater.git, which consists of a shell script to check your current public IP address and update your cloudflare DNS record to reflect the change - cd to the new repository and cp the shell script to a backup copy
- Make the following changes to the script, referencing the NetworkChuck video for the location of each piece of information
- edit
auth_emailto your cloudflare login email - edit
auth_methodto global - edit
auth key - edit
zone - edit
record nameto match the DNS record you made in cloudflare - set ttl to 1
- set proxy to true
- edit
Once all those changes are made, chmod -x the modified shell script
Cronjobs (Website)
You now need to create a cronjob to automatically run the DDNS script every couple of minutes. Just as before:
- Run
sudo crontab -eon the server - Add the following line to update your DNS every minute:
*/1 * * * * /bin/bash <abs path to script>where<abs path to script>is the absolute path to the modified DDNS script. - Restart cron:
sudo systemctl restart cron
Automatically pulling from Github
I already had a public repository storing this site on github since, as mentioned before, I was using Github pages previously. I wanted to use a private repository instead.
I first git clone the website repository to some local directory on the server labelled <dir>.
Permissions and Configurations
We now need to give Apache read and write access to a folder that will contain the site:
sudo mkdir /var/www/<directory>where<directory>is whatever you want. This is the directory that Apache will draw from to serve the websitesudo chown $USER:www-data /var/www/<directory>where$USERis the admin account. This will allow apache2 to have the same permissions as the admin with regards to this foldersudo chmod g+s /var/www/<directory>will enable all new directories created in/var/www/<directory>to maintain the same permissions as/var/www/<directory>sudo chmod -R ug+w /var/www/<directory>grants write access to all directories in/var/www/<directory>sudo cp /etc/apache2/sites-available/000-default.conf /etc/apache2/sites-available/*.confwhere*gets replaced by whatever you want- Within
*.conf, change theDocumentRootattribute to/var/www/<directory> - run
sudo a2ensite * && sudo a2dissite 000-default.confwhere you replace*with the name of your config file. This enables your site and disables the default Ubuntu landing page
- Within
At this point, all that is left to do is copying the website contents over:
cp -a <dir>/. /var/www/<directory>
and then restart Apache2
sudo service apache2 restart
To automate this process, I made two shell scripts. The first of these scripts is meant to run on the server. What it does is pull the repository from github and copy over the files to /var/www/<directory>:
cd <dir>
git pull
cp -r <dir>/. /var/www/<directory>
The second of these scripts build the website locally on my laptop, pushes the changes to Github, ssh into the server and run and remotely run the second script to update the website.
#!/bin/bash
if [[ -z $1 ]];
then
echo "Need a commit message"
exit
else
cd <local-git-repo-dir>
<build commands>
git add --all
git commit -m "$1"
git push
<SSH-Alias> "bash -s" < <RemotePullScriptdir>
fis
Conclusion
To summarize, we repurposed an old laptop into a server by flashing Ubuntu onto it, set up hardened SSH access, expanded the storage for remote backup, and deployed a website (this one) via Cloudflare. I might want to modify this workflow to be more automated; but that is for another day.