It is not a secret that I am using Gandi Simple Hosting to host sleeplessbeastie’s notes website. This solution is almost maintenance-free and does not require any additional work to use Jekyll, as hosting static content is dead simple (see shell script to automate build and upload process).
However, recently I started to feel limited by the inability to automatically build and publish scheduled posts. Now it is time to solve this issue by switching to VPS cloud hosting.
I will briefly describe the whole process of preparing a Debian-based VPS server to host a blog built using Jekyll static site generator.
Table of contents
- Prerequisites
- Create a regular user.
- Disable root login over ssh
- Install HTTP server
- Configure HTTP server
- Prepare git repository
- Track blog content
- Install Jekyll static site generator
- Create a simple automated build system
- Monitor server statistics
- Monitor web statistics
- Install and configure firewall
- Future improvements
- A few words at the end
Prerequisites
- Debian-based server
- Jekyll static web-site
- SSL certificate
Create a regular user.
At first, please verify that sudo
utility is already installed.
# which sudo
If the above command does not return path to the sudo
utility, then install it using the following command.
# apt-get install sudo
Create blogger
user and set the desired password.
# useradd -m -G sudo -s /bin/bash blogger
# passwd blogger
From now on, you can use sudo
to acquire root privileges.
Disable root login over SSH
I assume that the OpenSSH service is already installed and running, but the default Debian configuration will permit root login over SSH, so you need to change this setting to the reasonable one.
$ sudo sed -i '/PermitRootLogin/ s/yes/no/' /etc/ssh/sshd_config
Reload SSH configuration to apply a change.
$ sudo service ssh reload
You have disabled root login over SSH, so use recently created blogger
user.
Install HTTP server
I want to use Nginx with SPDY protocol support, which is not available directly in the Debian Wheezy repository. . Fortunately, we do not need to build it from source, but just use the wheezy-backports repository to get the latest stable Nginx version.
$ echo -e "deb http://cdn.debian.net/debian wheezy-backports main\ndeb-src http://cdn.debian.net/debian wheezy-backports main" | sudo tee /etc/apt/sources.list.d/wheezy_backports.list
Update the package list.
$ sudo apt-get update
Every package from the above repository is marked as not automatic but with automatic upgrades (see man apt-preferences
).
$ apt-cache policy nginx nginx: Installed: (none) Candidate: 1.2.1-2.2+wheezy2 Version table: 1.4.6-1~bpo70+1 0 100 http://cdn.debian.net/debian/ wheezy-backports/main i386 Packages 1.2.1-2.2+wheezy2 0 500 http://ftp.task.gda.pl/debian/ wheezy/main i386 Packages 500 http://security.debian.org/ wheezy/updates/main i386 Packages
Install the HTTP server using the recently added repository.
$ sudo apt-get -y -t wheezy-backports install nginx
Verify that server is up and running.
Configure HTTP server
Create a directory for static files.
$ sudo mkdir -p /var/www/blog.sleeplessbeastie.eu $ sudo chown www-data:www-data /var/www/blog.sleeplessbeastie.eu
Create <a href="http://www.sleeplessbeastie.eu" rel="nofollow">http://www.sleeplessbeastie.eu</a>
virtual host.
$ sudo vi /etc/nginx/sites-available/www
server { listen 80 default_server; server_name www.sleeplessbeastie.eu; return 301 $scheme://sleeplessbeastie.eu; }
Create blog.sleeplessbeastie.eu
virtual host.
$ sudo vi /etc/nginx/sites-available/blog
server { listen 80; listen 443 ssl spdy; server_name blog.sleeplessbeastie.eu; root /var/www/blog.sleeplessbeastie.eu; index index.html; location / { try_files $uri $uri/ =404; } ssl on; ssl_certificate blog.sleeplessbeastie.eu.pem; ssl_certificate_key blog.sleeplessbeastie.eu.key; ssl_session_timeout 5m; ssl_protocols SSLv3 TLSv1 TLSv1.1 TLSv1.2; ssl_ciphers "HIGH:!aNULL:!MD5 or HIGH:!aNULL:!MD5:!3DES"; ssl_prefer_server_ciphers on; }
Please remember to create private key and certificate files.
Enable recently created virtual hosts and disable the default one.
$ sudo unlink /etc/nginx/sites-enabled/default $ sudo ln -s /etc/nginx/sites-available/www /etc/nginx/sites-enabled/www $ sudo ln -s /etc/nginx/sites-available/blog /etc/nginx/sites-enabled/blog
Reload nginx
configuration.
$ sudo service nginx reload
Prepare git repository
Install git
revision control system.
$ sudo apt-get -y install git
Create a bare repository that will be used to store source code.
$ git init --bare git/blog.sleeplessbeastie.eu.git
Track blog content
Enter the current Jekyll blog directory on the local machine.
$ cd /path/to/jekkyll_blog_source_code
Create .gitignore
file, as we do not want to track _site
directory (generated website).
$ echo "_site/" | tee .gitignore
Init git
repository.
$ git init
Add remote location (using just ssh protocol) to the repository.
$ git remote add origin blogger@blog.sleeplessbeastie.eu:~/git/blog.sleeplessbeastie.eu.git
Add all files to the repository and commit changes.
$ git add --all
$ git commit -m "Initial import"
Push all changes to the remote.
$ git push --all
Additional notes – How to clone git repository?
You can clone the repository from everywhere using the following command.
$ git clone blogger@blog.sleeplessbeastie.eu:/home/blogger/git/blog.sleeplessbeastie.eu.git
Install Jekyll static website generator
Install required dependencies.
$ sudo apt-get install ruby ruby-dev libgsl-ruby rubygems
Install jekyll
.
$ sudo gem install jekyll
Create a simple automated build system
Create a simple shell script to automate the build process.
$ sudo vi /usr/bin/build_website.sh
#!/bin/sh # Clone git repository, build and publish Jekyll blog # git repository repo="/home/blogger/git/blog.sleeplessbeastie.eu.git" # temporary directory temp_dir="/tmp/$(date +"%d%m%Y_%H%M")" # destination directory dest_dir="/var/www/blog.sleeplessbeastie.eu" # path to jekyll executable jekyll="/usr/local/bin/jekyll" # clone repository git clone $repo $temp_dir # build jekyll web-site LC_ALL=C.UTF-8 $jekyll build --lsi -s $temp_dir -d ${temp_dir}/_site # fix permissions chown -R www-data:www-data ${temp_dir}/_site # publish web-site rsync -r --delete ${temp_dir}/_site/ $dest_dir # remove temporary directory rm -r $temp_dir
Set the execute permission for the above shell script.
$ sudo chmod +x /usr/bin/build_website.sh
Create cron entry to build and publish this website every day at 00:30.
echo "30 0 * * * www-data /usr/bin/build_website.sh > /dev/null" | sudo tee /etc/cron.d/website
Additional notes
Remember to set future
option to false
inside your _config.yml
blog configuration file to store future posts inside _posts
directory.
$ cat _config.yml | grep future future: false
Monitor server statistics
This step is purely optional but highly recommended as it will allow to monitor resource usage and avoid potential problems in the future.
Install collectd
system statistics collection daemon.
$ sudo apt-get install -y --no-install-recommends collectd rrdtool
Edit configuration file and adjust it to your needs.
$ sudo vi /etc/collectd/collectd.conf
Use the following Perl script to quickly generate HTML page with all rrd data gathered by collectd
.
/usr/share/doc/collectd-core/examples/collectd2html.pl
Additional notes – the resolution of the collected data
By default collectd
will use an interval of 10 seconds, but such granularity is not needed.
You can safely increase the interval to 30 or even 60 seconds.
Please remember to delete rrd files after altering the interval option.
$ sudo rm -rf /var/lib/collectd/rrd/*
Additional notes – collect Nginx performance data
Create a virtual host with nginx
status page.
$ sudo vi /etc/nginx/sites-available/stats
server { listen 80; server_name localhost; location /status { stub_status on; access_log off; allow 127.0.0.1; deny all; } }
Enable virtual host and reload nginx
configuration.
$ sudo ln -s /etc/nginx/sites-available/stats /etc/nginx/sites-enabled/
$ sudo service nginx reload
Enable nginx
plugin in collectd
configuration file.
LoadPlugin nginx <Plugin nginx> URL "http://localhost/status" </Plugin>
Additional notes – … illegal attempt to update using time … syslog messages
This issue can be solved very quickly – just read Debian Bug report #710658.
Monitor web statistics
This step is not essential; however, web statistics can provide a lot of interesting insight.
Install awstats
package.
$ sudo apt-get install -y awstats
Prepare configuration file – set SiteDomain
, LogFormat
and LogFile
settings.
$ sudo cp /etc/awstats/awstats.conf /etc/awstats/awstats.blog.sleeplessbeastie.eu.conf $ sudo sed -i '/SiteDomain/ s/""/"blog.sleeplessbeastie.eu"/' /etc/awstats/awstats.blog.sleeplessbeastie.eu.conf $ sudo sed -i '/LogFile/ s/LogFile=".*"/LogFile="\/var\/log\/nginx\/access.log"/' awstats.blog.sleeplessbeastie.eu.conf $ sudo sed -i '/LogFormat/ s/4/1/' awstats.blog.sleeplessbeastie.eu.conf
Disable automatic generation of static pages every night.
$ sudo sed -i "/AWSTATS_ENABLE_BUILDSTATICPAGES/ s/yes/no/" /etc/default/awstats
Rename default configuration file, so used shell scripts will not read it during execution time.
$ sudo mv /etc/awstats/awstats.conf /etc/awstats/awstats.default
Cron entry found in /etc/cron.d/awstats
file will update statistics every ten minutes.
Now you can use the following command to generate HTML pages.
/usr/share/awstats/tools/awstats_buildstaticpages.pl -config=blog.sleeplessbeastie.eu -dir=/var/www/statistics/ -diricons=/statistics/icons/
Additional notes – Where to find icons?
You use icons found in /usr/share/awstats/icon/
directory.
Install and configure the firewall
Configure the firewall to control traffic flow.
Install shorewall
firewall.
$ sudo apt-get install shorewall
Configure shorewall
firewall.
$ sudo vi /etc/shorewall/interfaces
# # Shorewall version 4 - Interfaces File # # For information about entries in this file, type "man shorewall-interfaces" # # The manpage is also online at # http://www.shorewall.net/manpages/shorewall-interfaces.html # ############################################################################### FORMAT 2 ############################################################################### #ZONE INTERFACE OPTIONS - lo ignore net all physical=+,optional
$ sudo vi /etc/shorewall/policy
# # Shorewall version 4 - Policy File # # For information about entries in this file, type "man shorewall-policy" # # The manpage is also online at # http://www.shorewall.net/manpages/shorewall-policy.html # ############################################################################### #SOURCE DEST POLICY LOG LIMIT: CONNLIMIT: # LEVEL BURST MASK $FW net DROP net all DROP
$ sudo vi /etc/shorewall/rules
# # Shorewall version 4 - Rules File # # For information on the settings in this file, type "man shorewall-rules" # # The manpage is also online at # http://www.shorewall.net/manpages/shorewall-rules.html # ################################################################################################################################################################################### #ACTION SOURCE DEST PROTO DEST SOURCE ORIGINAL RATE USER/ MARK CONNLIMIT TIME HEADERS SWITCH # PORT PORT(S) DEST LIMIT GROUP #SECTION ALL #SECTION ESTABLISHED #SECTION RELATED SECTION NEW SSH(ACCEPT) net $FW - - - - s:ssh:3/min:5 Ping(ACCEPT) net $FW HTTP(ACCEPT) net $FW HTTPS(ACCEPT) net $FW
$ sudo vi /etc/shorewall/zones
# # Shorewall version 4 - Zones File # # For information about this file, type "man shorewall-zones" # # The manpage is also online at # http://www.shorewall.net/manpages/shorewall-zones.html # ############################################################################### #ZONE TYPE OPTIONS IN OUT # OPTIONS OPTIONS fw firewall net ip
Start shorewall
firewall at the system boot.
$ sudo sed -i "/startup=0/ s/0/1/" /etc/default/shorewall
Issue stop
command, instead of clear
when executing /etc/init.d/shorewall stop
.
$ sudo sed -i "/SAFESTOP=0/ s/0/1/" /etc/default/shorewall
Additional notes – How to enable outbound traffic?
To enable outbound traffic execute the following commands.
$ sudo sed -i "/^\$FW/ s/DROP/ACCEPT/" /etc/shorewall/policy $ sudo shorewall restart
Additional notes – What about IPv6 traffic?
To filter IPv6
traffic install shorewall6
package.
Future improvements
- Use key-based authentication to protect against brute force attacks.
- Investigate
nginx
caching mechanism. - Create an additional virtual host for statistics.
- Use a log analyzer to block offending IP addresses.
- Hide Nginx version.
A few words at the end
Currently nginx
package in the repository is too old to safely enable SPDY protocol.
I ended up compiling recent nginx
version from the source code.
I have used the following configuration arguments and just replaced nginx
binary.
-with-cc-opt='-g -O2 -fstack-protector --param=ssp-buffer-size=4 -Wformat -Werror=format-security -D_FORTIFY_SOURCE=2' --with-ld-opt=-Wl,-z,relro --prefix=/usr/share/nginx --conf-path=/etc/nginx/nginx.conf --http-log-path=/var/log/nginx/access.log --error-log-path=/var/log/nginx/error.log --lock-path=/var/lock/nginx.lock --pid-path=/run/nginx.pid --http-client-body-temp-path=/var/lib/nginx/body --http-fastcgi-temp-path=/var/lib/nginx/fastcgi --http-proxy-temp-path=/var/lib/nginx/proxy --http-scgi-temp-path=/var/lib/nginx/scgi --http-uwsgi-temp-path=/var/lib/nginx/uwsgi --with-pcre-jit --with-http_ssl_module --with-http_stub_status_module --with-http_spdy_module --prefix=/opt
VPS Chef Cooking up the best in Virtual Private Servers.