Mastodon Setup
Objective
Private mastodon server for family, private by design with option to go public. That should be fine, right?
Setup
Ref - https://www.vultr.com/docs/installing-mastodon-on-centos-7
Install NodeJS
curl --silent --location https://rpm.nodesource.com/setup_8.x |bash
yum -y install nodejs
Install yarn
wget https://dl.yarnpkg.com/rpm/yarn.repo -O /etc/yum.repos.d/yarn.repo
yum -y install yarn
Install redis
yum -y install redis
systemctl start redis
systemctl enable redis
Compilation deps
yum -y install ImageMagick git libxml2-devel libxslt-devel gcc bzip2 openssl-devel zlib-devel gdbm-devel ncurses-devel autoconf automake bison gcc-c++ libffi-devel libtool patch readline-devel sqlite-devel glibc-headers glibc-devel libyaml-devel libicu-devel libidn-devel
yum -y groupinstall 'Development Tools'
PostGRES
yum install https://download.postgresql.org/pub/repos/yum/reporpms/EL-7-x86_64/pgdg-redhat-repo-latest.noarch.rpm
yum -y install centos-release-scl-rh
yum -y install postgresql11-server postgresql11-contrib postgresql11-devel
/usr/pgsql-11/bin/postgresql-11-setup initdb
vim /var/lib/pgsql/11/data/pg_hba.conf
local from `peer` to `trust`
2 x `ident` to `md5`
systemctl start postgresql-11
systemctl enable postgresql-11
passwd postgres
su - postgres
createuser mastodon
psql
ALTER USER mastodon WITH ENCRYPTED password 'DBPassword' CREATEDB;
\q
exit
yum -y install libpqxx-devel protobuf-devel
Mastodon user & Ruby
adduser mastodon -d /opt/mastodon
usermod -aG wheel mastodon
passwd mastodon
su - mastodon
# Everything below done as mastodon user
gpg --keyserver hkp://keys.gnupg.net --recv-keys 409B6B1796C275462A1703113804BB82D39DC0E3 7D2BAF1CF37B13E2069D6956105BD0E739499BDB
curl -sSL https://get.rvm.io | bash -s stable
source /opt/mastodon/.rvm/scripts/rvm
rvm install 2.6
rvm use 2.6
gem install bundler
cd ~
git clone https://github.com/tootsuite/mastodon.git app
cd ~/app
git checkout v3.1.5
bundle config build.pg --with-pg-config=/usr/pgsql-11/bin/pg_config
bundle install --deployment --without development test
yarn install --pure-lockfile
# Use output of this for matching values in .env.production
RAILS_ENV=production bundle exec rake mastodon:webpush:generate_vapid_key
cp .env.production.sample .env.production
Set LOCAL_DOMAIN to domain name (thefranks.club)
Set database information
Set Secrets
SECRET_KEY_BASE=1
OTP_SECRET=2
Set Vapid keys
Set SMTP server (Zoho TLS)
SAFETY_ASSURED=1 RAILS_ENV=production bundle exec rails db:setup
RAILS_ENV=production bundle exec rails assets:precompile
SSL (as root)
# Do as in SERVER.md
NGINX
cat << 'EOF' > /etc/nginx/http.d/thefranks.club.conf
server {
listen 443 ssl;
listen [::]:443 ssl;
server_name thefranks.club;
ssl_certificate /etc/ssl/nginx/fullchain-thefranks-club.pem;
ssl_certificate_key /etc/ssl/nginx/key-thefranks-club.pem;
keepalive_timeout 70;
sendfile on;
client_max_body_size 0;
root /opt/mastodon/app/public;
gzip on;
gzip_disable "msie6";
gzip_vary on;
gzip_proxied any;
gzip_comp_level 6;
gzip_buffers 16 8k;
gzip_http_version 1.1;
gzip_types text/plain text/css application/json application/javascript text/xml application/xml application/xml+rss text/javascript;
add_header Strict-Transport-Security "max-age=31536000";
location / {
try_files $uri @proxy;
}
location ~ ^/(emoji|packs|system/accounts/avatars|system/media_attachments/files) {
add_header Cache-Control "public, max-age=31536000, immutable";
try_files $uri @proxy;
}
location @proxy {
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 https;
proxy_set_header Proxy "";
proxy_pass_header Server;
proxy_pass http://127.0.0.1:3000;
proxy_buffering off;
proxy_redirect off;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
tcp_nodelay on;
}
location /api/v1/streaming {
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 https;
proxy_set_header Proxy "";
proxy_pass http://127.0.0.1:4000;
proxy_buffering off;
proxy_redirect off;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
tcp_nodelay on;
}
error_page 500 501 502 503 504 /500.html;
}
EOF
Systemd
cat << EOF > /etc/systemd/system/mastodon-web.service
[Unit]
Description=Mastodon Web Service
After=network.target
[Service]
Type=simple
User=mastodon
Group=mastodon
WorkingDirectory=/opt/mastodon/app
Environment="RAILS_ENV=production"
Environment="PORT=3000"
ExecStart=/bin/bash -lc 'bundle exec puma -C config/puma.rb'
TimeoutSec=15
Restart=always
[Install]
WantedBy=multi-user.target
EOF
cat << EOF > /etc/systemd/system/mastodon-queue.service
[Unit]
Description=Mastodon Queue Service
After=network.target
[Service]
Type=simple
User=mastodon
WorkingDirectory=/opt/mastodon/app
Environment="RAILS_ENV=production"
Environment="DB_POOL=5"
ExecStart=/bin/bash -lc 'bundle exec sidekiq -c 5 -q default -q mailers -q pull -q push'
TimeoutSec=15
Restart=always
[Install]
WantedBy=multi-user.target
EOF
cat << EOF > /etc/systemd/system/mastodon-api.service
[Unit]
Description=Mastodon Streaming
After=network.target
[Service]
Type=simple
User=mastodon
WorkingDirectory=/opt/mastodon/app
Environment="NODE_ENV=production"
Environment="PORT=4000"
ExecStart=/bin/npm run start
TimeoutSec=15
Restart=always
[Install]
WantedBy=multi-user.target
EOF
chmod +x /opt/mastodon
firewall-cmd --permanent --zone=public --add-port=4000/tcp
firewall-cmd --reload
systemctl enable mastodon-web mastodon-queue mastodon-api
systemctl start mastodon-web mastodon-queue mastodon-api
Fixing issues
# Redis < 4 in repos so Sidekiq was failing
yum install epel-release
yum remove redis
yum install rh-redis5.x86_64
systemctl start rh-redis5-redis
# Mail still not working but queue is
## Change all details of SMTP to stu@stuts.uk, no difference
## Looked into ActionMailer delivery errors
## Tried without TLS (587 -> 465)
## Enabled IMAP for my email account (through admin gui and mail settings gui)
## Linode restriction of SMTP ports
## Manually confirmed account through rails console
# Images are publicly available
## Enabled "Authorized Fetch" in .env.production
## Enabled "Whitelist Mode" as this may do the restrictions needed
## Note: perhaps it's the nginx config using public cache-control for images?
## A good ref for similar things: https://discourse.joinmastodon.org/t/completely-private-instance/1574/4
## Discussion on confidential hosting: https://github.com/tootsuite/mastodon/issues/9785
## Attempted commenting out the entire caching section for images in nginx (shrug)
## It should autheticate user for media (`before_action :authenticate_user!, if: :whitelist_mode?` in MediaController and MediaProxyController)
## Tried restarting everything (stop masto services, restart nginx, start masto services)
## Posted another image...
## No difference
## Asked Mark for help
New User Privacy
- Click “Edit Profile” from the timeline
- On the “Appearane” page select “Lock Account” (this gives you the power to decide who can follow you)
- Click Save Changes
- Go to Preferences -> Other in the left-hand menu
- Select “Hide Your Network” (this prevents anyone from seeing who you follow and who follows you)
- Set “Posting Privacy” to “Followers Only” (this allows only those you’ve accepted to follow you to see your content)
- Click Save Changes
Mark Notes:
paperclip configuration can be tweaked to store in S3, or to store in a different place in the filesystem, but the S3 support (by mastodon) still only works with the public-read ACL — you can configure it differently, but you’ll get the usual S3 “permission denied” stuff so it won’t really work
i think the only solution is to “hack” on an attachment serving controller — i’d probably go:
configure mastodon/paperclip to serve attachments from a different URL base (i.e. change PAPERCLIP_ROOT_URL hereish: https://github.com/tootsuite/mastodon/blob/44f88a334b1630fc236bdd6fbd4cde42f2882e21/.env.nanobox#L86
implement a route & controller that listens for GET requests to the new URL, does the appropriate auth stuff and then does one of:
.env.nanobox:86
- rails serves file statically (probably fine on low-volume system)
- some kind of “sign upstream URL” and redirect (i.e. that might be the approach if you were storing objects in S3)
- redirect to a still-publicly-available URL served by nginx — not sure this is much of a win tho. you could poss do a “copy to temporary area and periodically sweep older files”. seems complex tho.probs the first one. afaics, that approach is the only fix for your requirement
if you can get at the mastodon auth stack, you might be able to mount a rack app at an appropriate URL that does the file serving
Further Mark Talk
- Remove first 2 things from Nginx as they do public serving off stuff
- Allow serving of static files in rails
- Create a controller for the paths of the private files
- This should check authentication in the show method
- On unauthenticated, show an image that says “this image is on a private server” (this will help to address potential federation issues)
- Profit?