This tutorial will show you how to set up a blog using Cloudflare and Ghost. Cloudflare will improve performance and security by acting as a caching reverse-proxy.

What is Cloudflare?

Cloudflare is a service that acts as a middleman between a website and its end users. End users connect to Cloudflare servers, which retrieve content from the website's origin. Cloudflare can act as a firewall and allow/deny requests based on conditions, such as location. Lastly, Cloudflare can often improve performance by directly serving cached content to users without contacting the origin server.

How to use Cloudflare

Cloudflare has a free plan that provides DDoS protection and up to 3 page rules.

Register your account, and then add your first site.

Next, you need to log in to your domain registrar and set the domain to use Cloudflare servers.

Once the DNS has propagated, create the certificate/key for the origin server. This can be accessed in the crypto section. This certificate/key is used to secure the link between the origin server and cloudflare. Make sure you save this.

Next, we configure the cloudflare servers to provide a certificate to the origin server. Enable authenticated origin pulls


Next, in the DNS section, add the new IP address of the origin server

Protecting the admin interface

To improve the security of the blog, the admin interface will only be accessible from a specific autonomous system (only from your ISP networks, in other words). You can find your ISP AS number at bgp.he.net. Click firewall, and then add rule. The rule will block traffic if the path contains /ghost/ and the AS num does not equal 20473 (my isp)

Attempts to access the admin interface from other networks, will be blocked and logged, displaying the following message to clients

Configuring the origin server

I've made an installation script that will automate the whole process of configuring the origin server; all you need to do is add your key and certificate

The script does the following things:

1. Installs docker and docker compose

2. Installs and configures socat to serve the blog over TLS, checking the client provides a valid certificate; this means only cloudflare servers can access the origin server

3.  Configures a production Ghost environment with a separate MySQL database container using docker compose.

#! /bin/bash

mkdir /root/socat-ghost
#
# Put your certficate here
#
cat > /root/socat-ghost/origin-cert.pem << EOF
-----BEGIN CERTIFICATE-----
-----END CERTIFICATE-----
EOF

#
# Put your key here
#
cat > /root/socat-ghost/origin-key.pem << EOF
-----BEGIN PRIVATE KEY-----
-----END PRIVATE KEY-----
EOF

#
# Download origin ca
#
curl -fSsL https://support.cloudflare.com/hc/en-us/article_attachments/201243967/origin-pull-ca.pem --output /root/socat-ghost/origin-ca.pem

#
# Install and enable socat 
#
apt-get update
apt-get -y install socat 

#
# Socat systemd unit
#
cat > /etc/systemd/system/socat.service << EOF
[Unit]
After=network.target
[Service] 
Type=simple
Restart=always
ExecStart=/usr/bin/socat openssl-listen:443,su=nobody,certificate=/root/socat-ghost/origin-cert.pem,key=/root/socat-ghost/origin-key.pem,cafile=/root/socat-ghost/origin-ca.pem,verify=1,fork tcp-connect:0.0.0.0:2368
[Install]
WantedBy=multi-user.target
EOF
systemctl daemon-reload
systemctl enable socat.service 
systemctl start socat.service 

#
# Install and enable docker 
#
curl -fsSL https://get.docker.com | sh -
systemctl enable containerd.service docker.service
systemctl start docker.service

#
# Install docker-compose
#
curl -L "https://github.com/docker/compose/releases/download/1.23.1/docker-compose-$(uname -s)-$(uname -m)" -o /usr/local/bin/docker-compose
chmod +x /usr/local/bin/docker-compose

#
# Start ghost docker compose environment
#
mkdir /root/docker-ghost && cd /root/docker-ghost

#
# Docker-compose yml
#
cat > docker-compose.yml << EOF
version: "2"
services:
  ghost:
    image: ghost
    volumes:
      - ghostdata:/var/lib/ghost/content
    ports:
      - "2368:2368"
    environment:
      - url=http://test.etherarp.net
      - database__client=mysql
      - database__connection__host=db
      - database__connection__user=ghostdb
      - database__connection__password=secret
      - database__connection__database=ghostdb
      - VIRTUAL_HOST=test.etherarp.net,www.test.etherarp.net
      - VIRTUAL_PORT=2368
    depends_on:
      - db
    restart: unless-stopped
  db:
    image: mariadb
    volumes:
      - ghostdb:/var/lib/mysql
    environment:
      - MYSQL_ROOT_PASSWORD=supersecret
      - MYSQL_DATABASE=ghostdb
      - MYSQL_USER=ghostdb
      - MYSQL_PASSWORD=secret
    restart: unless-stopped
volumes:
  ghostdb:
  ghostdata:
EOF

#
# Start docker compose
#
/usr/local/bin/docker-compose up -d
systemctl start socat.service