Setting up Ghost with SSL on Fedora

To get your install of Ghost, use the following commands changing the file locations and usernames to meet your needs.

First start by installing the necessary packages

yum install nodejs nginx npm

Now go to /srv and extract the latest version of ghost there.

cd /srv
curl -L https://ghost.org/zip/ghost-latest.zip -o ghost.zip
unzip -uo ghost.zip -d ghost

Use npm to install Ghost's dependencies from /srv/ghost

npm install --production

Add the user to be used for this instance of Ghost

useradd -r ghost -U
chown -R ghost:ghost /srv/ghost

In /srv/ghost copy config.example.js to config.js and modify the beginning to meet your needs. Two additional parameters need to be added to the default config for SSL support. Here is an example snippet of the modified production section.

config = {
    // ### Production
    // When running Ghost in the wild, use the production environment
    // Configure your URL and mail settings here
    production: {
        url: 'http://example.com',
        urlSSL: 'https://example.com',
        forceAdminSSL: true,
        mail: {},
        database: {
            client: 'sqlite3',
            connection: {
                filename: path.join(__dirname, '/content/data/ghost.db')
            },
            debug: false
        },

    server: {
            // Host to be passed to node's `net.Server#listen()`
            host: '127.0.0.1',
            // Port to be passed to node's `net.Server#listen()`, for iisnode set this to `process.env$
            port: '2368'
        }
    },

For this install I'm sticking to the default of using sqlite. Now at this point you could also configure an existing mysql server by replacing the database section with someting like the following

database: {
    client: 'mysql',
    connection: {
        host: 'localhost',
        user: 'user-that-has-rights-to-ghost',
        password: 'secure-password',
        database: 'ghost',
        charset: 'utf8'
    }
}

but you may find that quite a bit of overkill after reading an excellent writeup for using Sqlite in production on DigitalOcean.

Use the following for /etc/systemd/system/ghost.service so you can setup Ghost to run on startup.

[Unit]
Description=ghost
After=network.target
[Service]
Type=simple
# Edit WorkingDirectory, User and Group as needed
WorkingDirectory=/srv/ghost
User=ghost
Group=ghost
ExecStart=/usr/bin/npm start --production
ExecStop=/usr/bin/npm stop --production
Restart=always
SyslogIdentifier=Ghost
[Install]
WantedBy=multi-user.target

All that is left is to start Ghost and enable it on boot

systemctl start ghost
systemctl enable ghost

It will be listening on 127.0.0.1:2368 by default which is perfect for passing to from Nginx.

If you haven't gotten your SSL certificate yet at this point and want a quick refresh, check out my post on generating a CSR for a quick example using Comodo's PositiveSSL.

The following is an example nginx configuration for proxying requests to our Ghost installation. I prefer a naked domain myself so you'll notice the redirects here. If you are unfamiliar with the implications of that this site has a great breakdown with example configurations for you to work with.

user  nginx;
worker_processes  1;

error_log  /var/log/nginx/error.log;

pid        /run/nginx.pid;


events {
    worker_connections  1024;
}


http {
    ## If you want to temporarily limit access to the server to one IP use the following two lines
    #allow 0.0.0.0;    #Your IP Address to allow
    #deny all;        #Block the rest

    include       /etc/nginx/mime.types;
    default_type  application/octet-stream;

    access_log  /var/log/nginx/access.log;

    sendfile        on;
    #tcp_nopush     on;

    #keepalive_timeout  0;
    keepalive_timeout  65;

    gzip  on;

    index   index.html index.htm;

    server {
        listen       80;
        server_name  example.com;
        root         /usr/share/nginx/html;

        ## redirect www to nowww
          if ($host = 'www.example.com' ) {
             rewrite  ^/(.*)$  http://example.com/$1  permanent;
          }

        location / {
                proxy_set_header X-Real-IP $remote_addr;
                proxy_set_header HOST $http_host;
                proxy_set_header X-Forwarded-Proto $scheme;

                proxy_pass http://127.0.0.1:2368/;
                proxy_buffering off;
        }

        location /robots.txt {
            return 200 "User-agent: *\nDisallow:";
        }




        # redirect server error pages to the static page /40x.html
        #
        error_page  404              /404.html;
        location = /40x.html {
        }

        # redirect server error pages to the static page /50x.html
        #
        error_page   500 502 503 504  /50x.html;
        location = /50x.html {
        }
    }

   # HTTPS server
    #
    server {
    listen       443;
        server_name  example.com;
        root         /usr/share/nginx/html;
        access_log  /var/log/nginx/ssl-access.log;
        error_log  /var/log/nginx/ssl-error.log;

     ## redirect www to nowww
      if ($host = 'www.example.com' ) {
         rewrite  ^/(.*)$  https://example.com/$1  permanent;
      }

        ssl                  on;
        ssl_certificate      /etc/nginx/ssl/example_com.chain.crt;
        ssl_certificate_key  /etc/nginx/ssl/example_com.key;
        ssl_dhparam          /etc/nginx/ssl/whit.dhparam;


        ssl_session_timeout  5m;

        #Following settings are reasonable post POODLE (https://poodle.io)
        ssl_prefer_server_ciphers   on;
        ssl_protocols TLSv1.2;
    ssl_ciphers "EECDH+ECDSA+AESGCM EECDH+aRSA+AESGCM EECDH+ECDSA+SHA384 EECDH+ECDSA+SHA256 EECDH+aRSA+SHA384 EECDH+aRSA+SHA256 EECDH+aRSA+RC4 EECDH EDH+aRSA !RC4 !aNULL !eNULL !LOW !3DES !MD5 !EXP !PSK !SRP !DSS";

        location / {
                #Adjust this to match media upload size requirements
                client_max_body_size 1m;
                proxy_set_header X-Real-IP $remote_addr;
                proxy_set_header HOST $http_host;
                proxy_set_header X-Forwarded-Proto $scheme;

                proxy_pass http://127.0.0.1:2368/;
                proxy_buffering off;
        }

        location /robots.txt {
            return 200 "User-agent: *\nDisallow:";
        }

        # redirect server error pages to the static page /40x.html
        #
        error_page  404              /404.html;
        location = /40x.html {
        }

        # redirect server error pages to the static page /50x.html
        #
        error_page   500 502 503 504  /50x.html;
        location = /50x.html {
        }


    }
}

If you are having trouble accessing the server remotely remember that firewalld comes enabled by default, so you may need to look into how to open the neccessary ports if you haven't done so already.

Also note that while I left the static error pages in, Ghost's custom error pages will take precedence when they are returned.

Waldo

A *nix enthusiast and accidental programmer interested in sharing whatever tidbits I learn, more or less for my own reference.

The Evergreen State