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 the time to solve this issue by switching to the VPS cloud hosting.
I will briefly describe the whole process of preparing Debian-based VPS server to host blog built using Jekyll static site generator.
Table of contents
- Prerequisites
- Create 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 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 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 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 OpenSSH service is already installed and running, but 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 applied 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 Debian Wheezy repository. . Fortunately, we do not need to build it from source, but just use wheezy-backports repository to get 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 package list.
$ sudo apt-get update
Do not worry as it will not mess everything up, because 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 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 directory for static files.
$ sudo mkdir -p /var/www/blog.sleeplessbeastie.eu $ sudo chown www-data:www-data /var/www/blog.sleeplessbeastie.eu
Create www.sleeplessbeastie.eu
virtual host.
$ sudo vi /etc/nginx/sites-available/www
server { listen 80 default_server; server_name www.sleeplessbeastie.eu; return 301 $scheme://blog.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 which will be used to store blog source code.
$ git init --bare git/blog.sleeplessbeastie.eu.git
Track blog content
On local machine enter current Jekyll blog directory.
$ 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 [email protected]:~/git/blog.sleeplessbeastie.eu.git
Add all files to 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 repository from everywhere using the following command.
$ git clone [email protected]:/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 simple automated build system
Create simple shell script to automate 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 web-site 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 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 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 firewall
Now it is the time to setup 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 additional virtual host for statistics.
- Use log analyzer to block offending IP addresses.
- Hide nginx version.
A few words at the end
Currently nginx
package in 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