Ghost + Cloudinary: Deliver automatically optimized images from a CDN for free

Ghost + Cloudinary: Deliver automatically optimized images from a CDN for free

Thursday, June 6, 2019

Here's the current state of my blog infrastructure:

  • Digital Ocean Kubernetes --> Hosts Ghost backend in a Docker container
  • Netlify --> Hosts Gatsby static site generator, and builds html, .css, and .js static assets, but not the images, those are still served directly from Ghost on DO. Gatsby uses Ghost as the source for it's content.
  • Cloudfare -> Hosts cached images. Cloudfare is a reverse proxy that automatically caches images, as well as other static assets served by Netlify. Provides additional DDOS and DNS protection, in the form of DNSSEC.

Here are my current issues:

  • As a common practice in enterprise infrastructure, you want to isolate your origin/backend from the Internet. If it's reachable from the internet it could be  be attacked via Distributed-Denial of Service (DDOS), or accidentally hotlinked into a slashdot article, which would bypass the flood protection provided by the upstream CDNs (Netlify, Cloudfare, etc.). You could call getting slashdotted a form of non-malicious DDOS.
  • My CMS backend is still open for business to the public (via domain name and IP address).
  • Cloudfare is proxy-caching images originating from the backend CMS, but you could find a way to potentially bypass Cloudfare since domain name from which the images are coming is not masked.

HTML, CSS, and JavaScript is currently protected via Netlify and CloudFront. Images are protected by CloudFare (via proxy cache), but the origin DNS name is still exposed on the image source URL and therefore vulnerable. CloudFare masks the origin server IP address (which is a good thing), but if the domain name is still exposed, I'm afraid this kinda makes the IP address masking moot, since you can hit the origin directly by looking at the image source URL.

Here are my options:

  • I can mask the origin DNS name by switching completely from CloudFare to Amazon CloudFront, forgoing DNSSEC in the process. CloudFront has the capacity to mask the origin via a DNS CNAME (cdn.mydomain.com), as well host the image assets in Amazon S3 in a push configuration (good for backup).
  • I could try adding Amazon CloudFront, in addition to CloudFare and Netlify. This would bring the number of CDNs to manage and cache bust when developing/troubleshooting from 2 to 3.
  • Or, I could give Cloudinary a try.

Cloudinary is a dedicated image Content Delivery Network, with additional free benefits that traditional CDNs such as CloudFare and CloudFront may not provide for free, such as automatically resizing image assets for high resolution retina displays.

Here's some information about Cloudinary:

This Ghost plugin provides support for Cloudinary:

It's been updated as recently as a month ago, from June, 2019. And has seen significant updates across it's codebase in the last year according to Github.

Problem: It's tested for Ghost 1.x, not Ghost 2.x. And we're rolling on the edge, with Ghost 2.x.

Eexit's Git repo is a fork from:

That, too, was made for Ghost 1.x.

And the original project, https://github.com/sethbrasile/ghost-cloudinary-store, is now deprecated (no longer maintained).

On the plus side, eexit's Cloudinary repo provides instructions for working with Dockerized Ghost installs, which is our use case.

On the down side, using this repo on our current Ghost 2.x install would be rolling the dice, but it's something that I think it's worth trying out;

On a side note, I don't have either a local or online staging Kubernetes environment where I can test this, which is something I have should/probably must address sooner than later. If I roll it out on my current Kubernetes environment it could blow it up, and I could potentially lose my extensive, painstaking 4 days of blog posts.

On another side note, if the version 1.x of the plugin doesn't pan out, it would be an opportunity to bring it up to date, contribute something to the open source community, and get familiarized with the internals of the Ghost source code, something I haven't done yet - not a bad outcome. I just don't think I have the necessary bandwidth for this. Something to take into consideration.

Anyways, those are my thoughts for today.

I guess if we go the Cloudinary route there will be another, more technical blog post detailing the gory details (which is why I created this blog in the first place).