This is part two of Self Hosting an Azure Web Server blog. The journey will continue first with setting up a private github repository for version control and website backup. DNS will be integrated tying together the domain main and server IP address. TLS will be implemented enabling HTTPS. Various system hardening configurations and techniques will be implemented and the web site code will be edited and published.
Website Backup Repository with Git
To back up the website and create a version control system, I created a private github repository called securitykartoshka.com. That way I will always have a copy of the website in case I need to move my hosting services or experience data loss.
I've downloaded the Bootstrap template file, unzipped the contents, and uploaded those files to my repository for the initial commit.
Transferring the website files to the web server will be accomplished using SCP. The server is headless (i.e., no GUI) and because SSH is already active this is the easiest and most secure option. The root Apache web server directory is /var/www/html. Like with SSH, the -i option specifies a private key path. The -r option recursively copies all the files at the source to the destination.
scp.exe -i .\kartoshkasrv.pem -r .\startbootstrap-clean-blog-gh-pages\startbootstrap-clean-blog-gh-pages mariia@20.106.202.118:/var/www/html/
However, the SCP command failed.
To fix the SCP failure the permissions at /var/www/html must allow the mariia user to write to it. To do this, the chown command changes the owner of the directory to the mariia account and the group to www-data, which is the default user account and group for the Apache web server. Then the permissions of the /var/www/html directory will be modified (using chmod) giving mariia read/write/execute (RWX), www-data RX, and nothing for others. For files in the html directory mariia is granted RW, www-data R, and nothing for others. Collectively this ensures that the mariia user can add or edit website content and that the Apache www-data group can only read it. Should the web server get compromised, www-data will have minimal permissions.
This shows the permissions of the directory and files before and after the modifications where everything is owned by the root user and group.
sudo chown -R mariia:www-data /var/www/html
sudo find /var/www/html -type d -exec chmod 750 {} \;
sudo find /var/www/html -type f -exec chmod 640 {} \;
After running the commands the current html directory (designated by .) shows user mariia has RWX and the www-data group has RX. A file within that directory (index.html) shows user mariia has RW permissions and the www-data group only has R.
Trying the SCP command again.
This is confirmed by viewing the template website content as it was successfully served at the known server IP address.
DNS Configuration
The purchased domain name must be linked to the web server IP via DNS. To do this the DNS records can be edited at the GoDaddy registrar using the provided web management portal.
The DNS A record is used to map the domain name to the server IPv4 address. I set it with a time to live (TTL) of one hour. This is the amount of time a DNS resolver will cache the A record before it would perform a new recursive DNS query.
I also added a canonical name (or CNAME) record for the www subdomain. A CNAME record is an alias name and in this case ensures that if a user goes to www.securitykartoshka.com it is equivalent (or technically redirected) to going to securitykartoshka.com.
Local Git Integration
At the beginning of this post a GitHub repository for the website was created. However, I want to be able to edit and work on it locally to pull the latest branch or push new commits. I have git installed locally and I configured the global name and email settings. This is needed for working with git to access the private repo and committing changes.
git config -global user.name "friedkartoshka"
git config -global user.email "------------@gmail.com"
For remote access to GitHub they require the usage of a personal access token. I generated this token on my GitHub account that can be used for authentication with my local git instance.
The template website files I uploaded to the web server and to the GitHub repository were the same with no edits. Moving forward I will only edit my website content from the git repository. To have a local working copy I cloned the repository. This is a private repo so only someone with access to my GitHub account can clone this.
git clone https://github.com/friendkartoshka/securitykartoshka.com.git
TLS Time - Enabling HTTPS
To add HTTPS support to my website, I'll install a free TLS certificate from the Electronic Frontier Foundation (EFF) Let's Encrypt project. These certificates renew every 90 days and they have a utility called Certbot that when setup, automatically requests a Let's Encrypt certificate for my domain. It also renews the certificate as necessary. It uses Ubuntu's snap package management system.
I need to edit the /etc/apache2/sites-available/000-default.conf file's default VirtualHost config by adding a ServerName and ServerAlias.
ServerName securitykartoshka.com
ServerAlias www.securitykartoshka.com
Installation guidance said to first ensure that no existing Certbot packages exist on Ubuntu because snap will be used.
sudo apt remove certbot
Now it's time to install Certbot using snap, not apt. After that the creation of a symbolic link that points /usr/local/bin/certbot to the actual Certbot binary located in /snap/bin/certbot is required. This ensures the certbot executable is in my user account $PATH and has the permission to run.
sudo snap install --classic certbot
sudo ln -s /snap/bin/certbot /usr/local/bin/certbot
Getting and installing the certificate, plus editing the Apache configuration was next to enable HTTPS support. This was a longer dialog with multiple steps (plus I had to quit and start over once) so no long screenshot for this one. However, here's the command to kick off the process.
sudo certbot --apache
Certbot has the --dry-run option to test and ensure that the Certbot automatic certificate renewal function is working. Certificate renewal is set as a cron job (which is like a scheduled task on Windows).
sudo certbot renew --dry-run
Going to https://securitykartoshka.com confirms that HTTPS is active. It can't be pictured, but I also confirmed that going to http://securitykartoshka.com redirects to HTTPS. Both of these were confirmed for the www subdomain as well.
In Chrome you can see that the site has a secure connection and that the certificate if valid. Viewing the certificate details also shows it was issued by Let's Encrypt.
Configuration Hardening
Ubuntu server should have automatic updates enabled by default. However, I want to confirm this by making sure the unattended-upgrades package is installed. It was already installed so no new actions were taken.
sudo apt install unattended-upgrades
By default the unattended-upgrades package installs security updates and the related dependencies. It doesn't install new versions, features, or other unessential patches. However, I want the server to update all software regularly as it will not be regularly managed. To activate this a line in the /etc/apt/apt.conf.d/50unattended-upgrades file has to be uncommented in the Allow-Origins section.
The operating system and Apache version information returned by the server can be restricted. To do this, I modified the /etc/apache2/conf-available/security.conf file and changed the ServerTokens and ServerSignature variables.
I also want to restrict directory browsing so users cannot view the web server root directory files. This requires changing the /etc/apache2/apache2.conf file and adding a minus sign (-) in front of the Index option.
PHP configuration hardening is controlled in the /etc/php/8.3/apache2/php.ini file. This includes the suppression of PHP display errors (display_errors) to the user, logging PHP errors (log_errors), and ensuring dangerous PHP functions are disabled (disable_functions).
Hardening SSH requires configuration setting changes to the /etc/ssh/sshd_config file. This included the restriction of the root account login, no password and only public key authentication, no empty passwords, and disabling X11 forwarding.
The SSH service has to be restarted for the changes to take effect. It's also advised that after doing this to open a new shell and attempt another simultaneous SSH connection. This tests to ensure the changes don't lock you out. If a problem is encountered, you still have your existing active session to make config adjustments ensuring continued system access.
sudo systemctl restart ssh.service
The fail2ban program will also be installed. This helps prevent brute force attacks and things like bots trying to access publicly exposed SSH.
sudo apt install fail2ban
fail2ban works on the concept of jails. If a given IP address violates the designated rules it gets added to the related jail (i.e., blocked). The fail2ban SSH jail settings will be implemented to allow a maximum number of authentication retries of five. If a failure happens for a given IP five times (over a 10-minute window by default) then it will be blocked for an hour (3600 seconds) before it can try again. The 10-minute window can be adjusted by adding a findtime = parameter to the config. The recommended guidance is to not edit the default jail configuration file /etc/fail2ban/jail.d/defaults-debian.conf as this can be overwritten with future apt package manager fail2ban updates. Instead, the use of .local files should be used. I'll create sshd.local in the jail.d directory.
The fail2ban service must be restarted for these changes to take effect. As with the SSH config changes, I'll open a second shell and ensure I still have remote SSH access with the new fail2ban settings prior to performing other tasks.
sudo systemctl restart fail2ban.service
I could also change the default SSH port from 22 to something else. This would need to be coordinated in the SSH config file, the Azure VM network settings, and for fail2ban. This would provide a little security by obscurity by reducing the scan noise from bots, etc. that may only try the top common internet-exposed ports. For instance, if you run the SYN (-sS) scan in nmap, it defaults to using the top 1000 most common TCP ports unless you add other scan options. However, this is something I'll consider at a later point.
Website Authoring
At least for now, my cloud VM server is complete. Now it's time to actually build the website content. This is primarily HTML and a little CSS as of now that I'm actually coding. I did add PHP capability and at a later point may include some code for that as well. After formatting the website in general and working on the first blog post (including adding images) I wanted to save and commit these changes to my GitHub repo. I first check the status of these changes using the git status command. These show me any files that have changed since the last commit.
I'm happy with that so I stage the changes using the git add and and then commit the changes to the repository by, unsurprisingly, using the git commit command. The -m allows me to add a comment about the commit being made.
After doing this the changes are only on my local system. My remote GitHub repository has no record of these changes. To sync them I can upload these changes, including the addition/deletion of any files, using the git push commmand. The effectively backs up my website to a cloud resource. Regardless of what happens to my cloud VM or local system, I'll be able to recover my site content in full. All I'd have to do is clone the repo and then copy it to a new web server's root directory (/var/www/html/ for Apache).
Git takes care of version control and backup but none of the new files or file changes are on my web server. To actually make the changes active they have to be copied. As before, SCP is the tool for this. Since this is the initial deployment of the site, various files have been edited in various directories for several differnt SCP commands were used to upload everything. The first upload is all of the blog post 1 image files that need to be saved at /var/www/html/assets/img/001.
scp.exe -i ..\kartoshkasrv.pem -r .\assets\img\001 mariia@20.106.202.118:/var/www/html/assets/img/
A few other images for the site are uploaded.
scp.exe -i ..\..\..\kartoshkasrv.pem home-bg.jpg russet_high_res.png mariia@20.106.202.118:/var/www/html/assets/img
The favicon.ico file is uploaded. This is the small icon displayed next to the site title on a browser tab.
scp.exe -i ..\..\kartoshkasrv.pem favicon.ico mariia@20.106.202.118:/var/www/html/assets/
All the HTML web content is uploaded. Most of this are the initial edits of core files as well as the first blog post entry in post001.html.
scp.exe -i ..\kartoshkasrv.pem about.html contact.html index.html post001.html postXXX_template.html mariia@20.106.202.118:/var/www/html/
Finally the website edits can be confirmed by browsing to securitykartoshka.com over the internet. The index.html is the default file server on the site homepage.
Cost
Looking at the cost of running my server for about a month using the standard B2ts v2 virtual machine in East (US) zone, it was $15.57. That's more than double what one might assume based on the original VM pricing of $7.59/month. One must also take into account storage costs and network related things such as my need for a public static IP.



































