Send your GhostCMS posts to Mastodon
Today I show a way how to redirect GhostCMS posts directly to Mastodon.
Creating new posts on the website by hand to social networks is possible, but has no charm.
While I'd love to use pre-built solutions, Zapier, for example, doesn't allow webhooks with a free account (the paid accounts are a cheek for what's offered). Also the direct forwarding to Mastodon via GhostCMS fails, because you can't change the payload with GhostCMS.
General design
Ghost CMS triggers new posts and sends a webhook with the new post to a local Python Flask instance.
The Flask script fetches the relevant data from the payload and creates a Mastodon post. We use Mastodon.py for this.
The Flask script is secured by UWSGI.
As a bonus, GhostCMS tags are converted to Mastodon hashtags.
Remote use / security
By default, the webhook endpoint should not be accessible from outside.
Access to the webhook is unencrypted, because GhostCMS does not allow access (without major intervention) to self-signed certificates.
If you are not running the instance on the same server as Ghost CMS, you need to encrypt the connection via https (e.g. using a reverse proxy).
Your GhostCMS must then be added to the trusted proxies list in .secrets/TRUSTED_PROXIES (see entries for Docker below).
1. Create Mastodon Application
Login to your Mastodon instance (i.e. mastodon.social).
After registration you can find the item development under preferences. Here you create an application with the following input:
The only check you need to set is at "write:statuses". Leave all others unchecked!
After you click on "submit", you will find the required access token under "Development":
You don't need the remaining tokens.
2. Enable webhooks in Ghost CMS
Log in to your Ghost CMS instance and open the admin settings. Create a new custom integration with any name.
At the bottom you will find the item "Add webhook". This is what we need.
Create a webhook endpoint with your local accessible IP and port 5000 (you can change the port later).
If you are using the docker compose setting with fixed ip, that would be the IP 10.9.9.99, thus http://10.9.9.99/webhook
On bare metal installations it would be localhost/127.0.0.1, thus 127.0.0.1/webhook
It is important that you select "posts published" as trigger and not "posts created". Otherwise you will send unfinished posts to Mastodon.
Don't forget to click on save in the upper right corner.
3. Docker magic
Create a folder .secrets in your Docker folder and docker-compose.yml file.
For passwords, tokens, etc we need four additional files in the .secrets folder.:
Populate the files in the .secrets directory as follows:
Copy your Mastodon token to MASTODON_ACCESS_TOKEN.
Copy your Webhook token from Ghost CMS to WEBHOOK_SECRET.
Enter in MASTODON_BASE_URL the address of your Mastodon instance, e.g. https://mastodon.social .
At TRUSTED_PROXIES you enter a list of IPs that should have access to your webhook instance. By default, only localhost and access via Docker subnet from the Docker compose example is entered with a fixed IP, i.e. 10.9.9.0/24.
You can set up your docker-compose.yml as follows:
version: '3.1'
secrets:
WEBHOOK_SECRET:
file: ${PWD}/.secrets/WEBHOOK_SECRET
MASTODON_ACCESS_TOKEN:
file: ${PWD}/.secrets/MASTODON_ACCESS_TOKEN
MASTODON_BASE_URL:
file: ${PWD}/.secrets/MASTODON_BASE_URL
TRUSTED_PROXIES:
file: ${PWD}/.secrets/TRUSTED_PROXIES
services:
ghostcms2mastodon:
image: okxo/ghostcms2mastodon:latest
restart: always
ports:
- 127.0.0.1:5000:5000
secrets: [WEBHOOK_SECRET,MASTODON_ACCESS_TOKEN,MASTODON_BASE_URL,TRUSTED_PROXIES]
environment:
WEBHOOK_SECRET: /run/secrets/WEBHOOK_SECRET
MASTODON_ACCESS_TOKEN: /run/secrets/MASTODON_ACCESS_TOKEN
MASTODON_BASE_URL: /run/secrets/MASTODON_BASE_URL
TRUSTED_PROXIES: /run/secrets/TRUSTED_PROXIES
networks:
vpcbr:
ipv4_address: 10.9.9.99
tty: true
networks:
vpcbr:
driver: bridge
ipam:
config:
- subnet: 10.9.9.0/24
gateway: 10.9.9.1
Start your docker stack with:
docker compose up -d
Alternatively with docker run:
Create network:
docker network create --subnet=10.9.9.0/24 webhookSubnet
Run docker instance with:
docker run -d -p 5000:5000/tcp -v "${PWD}/.secrets/WEBHOOK_SECRET:/run/secrets/WEBHOOK_SECRET:ro" -v "${PWD}/.secrets/MASTODON_ACCESS_TOKEN:/run/secrets/MASTODON_ACCESS_TOKEN:ro" -v "${PWD}/.secrets/MASTODON_BASE_URL:/run/secrets/MASTODON_BASE_URL:ro" -v "${PWD}/.secrets/TRUSTED_PROXIES:/run/secrets/TRUSTED_PROXIES:ro" --net webhookSubnet --ip 10.9.9.99 okxo/ghostcms2mastodon
NGINX reverse proxy
Create an upstream in /etc/nginx/nginx.conf
upstream mastodonwebhook {
server 127.0.0.1:5000;
keepalive 64;
}
Add webhook location to your GhostCMS NGINX conf (in sites-availabe or in conf.d folder):
location /webhook {
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_set_header X-Forwarded-Host $http_host;
proxy_set_header X-Forwarded-Uri $request_uri;
proxy_set_header X-Forwarded-Ssl on;
proxy_redirect http:// $scheme://;
proxy_http_version 1.1;
proxy_set_header Connection "";
proxy_cache_bypass $cookie_session;
proxy_no_cache $cookie_session;
proxy_buffers 64 256k;
# If behind reverse proxy, forwards the correct IP \
set_real_ip_from 10.0.0.0/8;
set_real_ip_from 172.0.0.0/8;
set_real_ip_from 10.9.0.0/16;
set_real_ip_from 192.168.0.0/16;
set_real_ip_from fc00::/7;
real_ip_header X-Forwarded-For;
real_ip_recursive on;
proxy_pass http://mastodonwebhook;
}
Restart NGINX.