Deploy Ghost under Raspbian with MySQL / nginx / Let's Encrypt / strong SSL
Introduction
Ghost is a wonderful blogging platform developped with node.js. It's easy to use, fast (You mean, like DC COMICS Flash ? Yeah dude ! Superpowers !) it manages SEO, mardown, and the default theme : Casper is sooooooo beautiful.
So now, we'll learn how to setup Ghost on your Raspberry PI, with Raspbian (of course !), nginx (because it's a damn good and fast http server), and secure it with SSL, but GREAT SSL, the kind of SSL that is rated A on ssllabs ! And... everything self-hosted at home !
__Ok let's do it !__
Setup our Node.js environment :
First of all, Raspbian does not provide by default what we need, so we'll have to download our components elsewhere, but it's fine 'cause Linux users are a big familly and we have many friends able to help us.
What do we need ? Ghost helps us to find our way ...
Node.js >=6.9 <7.* Recommended
Ok... If you say so :
$ sudo curl -sL https://deb.nodesource.com/setup_6.x | sudo bash -
$ sudo apt-get install -y nodejs
You should see something like this :
$ node -v
v6.11.0
$ npm -v
3.10.10
It was so difficult, my fingers are sweating... but i'm a warrior, so let's continue. Let me think... Ghost needs Sqlite3 support (even if we won't use it) !
$ sudo apt-get install sqlite3
Setup and configure our MySQL server/database :
Don't pass out, this step is easy :
$ sudo apt-get install mysql-server
[enter password]
$ mysql_secure_installation
$ mysql -u root -p
> create database ghost;
> CREATE USER 'ghost'@'localhost' IDENTIFIED BY 'password';
> grant create,delete,insert,select,update,alter ON ghost.* TO 'ghost'@'localhost';
> flush privileges;
> quit
Dude what are you doing ? We spoke about Ghost, remember ? Oh yeah, let's download and deploy it !
Ghost installation :
$ mkdir ~/blog && cd ~/blog
$ curl -LOk https://ghost.org/zip/ghost-latest.zip
$ unzip ghost-latest.zip
Boom boom ! Setup everything we need...
$ sudo npm install --production
How should we run that... forever, for eternity, as long as the power is ON, as long as I want to live... ?! Well, Forever will do something for you :
$ sudo npm install forever
Should I configure something ? Yeah, it can be good :
$ mv config.example.js config.js
$ vi config.js
OMG it's full of stars.... What ? are your drunk ? Just edit what you need, and let's get over with...
config = {
// ### Production
// When running Ghost in the wild, use the production environment.
// Configure your URL and mail settings here
production: {
url: 'https://blog.cagedmonster.net',
mail: {},
database: {
client: 'mysql',
connection: {
host: 'localhost',
user: 'ghost',
password: 'password',
database: 'ghost',
charset: 'utf8'
},
debug: true
},
server: {
host: '127.0.0.1',
port: '2368'
}
},
Time to run the blog, my hands are shaking....
$ NODE_ENV=production ~/blog/node_modules/forever/bin/forever start index.js
We'll create a crontab task running your app when the system starts :
$ crontab -e
@reboot cd ~/blog/ && NODE_ENV=production ~/blog/node_modules/forever/bin/forever start index.js
What happend, is everything OK ? Of course not, let's broadcast to the whole world !
Strong SSL with Let's encrypt :
$ cd /etc/ssl/
$ sudo openssl dhparam -out dhparams.pem 4096
$ cd ~ && wget https://dl.eff.org/certbot-auto
$ chmod a+x ./certbot-auto
$ ./certbot-auto certonly
nginx configuration :
$ sudo apt-get install nginx-full
$ sudo vi /etc/nginx/ssl.conf
ssl_prefer_server_ciphers on;
ssl_certificate /etc/letsencrypt/live/<yourdomain>/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/<yourdomain>/privkey.pem;
ssl_protocols TLSv1 TLSv1.1 TLSv1.2;
ssl_dhparam /etc/ssl/dhparams.pem;
ssl_ciphers "EECDH+AESGCM:EDH+AESGCM:ECDHE-RSA-AES128-GCM-SHA256:AES256+EECDH:DHE-RSA-AES128-GCM-SHA256:AES256+EDH:ECDHE-
RSA-AES256-GCM-SHA384:DHE-RSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-SHA384:ECDHE-RSA-AES128-SHA256:ECDHE-RSA-AES256-SHA:ECDHE-
RSA-AES128-SHA:DHE-RSA-AES256-SHA256:DHE-RSA-AES128-SHA256:DHE-RSA-AES256-SHA:DHE-RSA-AES128-SHA:ECDHE-RSA-DES-CBC3-
SHA:EDH-RSA-DES-CBC3-SHA:AES256-GCM-SHA384:AES128-GCM-SHA256:AES256-SHA256:AES128-SHA256:AES256-SHA:AES128-SHA:DES-CBC3-
SHA:HIGH:!aNULL:!eNULL:!EXPORT:!DES:!MD5:!PSK:!RC4";
ssl_ecdh_curve secp384r1;
ssl_session_cache shared:SSL:10m;
ssl_session_tickets off;
ssl_stapling on;
ssl_stapling_verify on;
resolver 8.8.8.8 4.4.4.4 valid=300s;
resolver_timeout 5s;
add_header Strict-Transport-Security "max-age=63072000;includeSubdomains; preload";
add_header X-Frame-Options DENY;
add_header X-Content-Type-Options nosniff;
$ sudo vi /etc/nginx/sites-enabled/default
server {
listen 80;
listen [::]:80;
server_name blog.cagedmonster.net;
return 301 https://$server_name$request_uri;
}
server {
listen 443 ssl;
listen [::]:443 ssl;
server_name blog.cagedmonster.net;
include ssl.conf;
location / {
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_pass http://127.0.0.1:2368/;
}
location /ghost {
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_pass http://127.0.0.1:2368/ghost;
allow 192.168.1.0/24;
allow 2a01:cb06:3e0:eb00::/56;
deny all;
}
}
Start nginx and manage your blog :
$ sudo service nginx start
Enjoy ;)