adapt code from mastodon-ansible

This commit is contained in:
Antonio J. Delgado 2022-11-19 11:10:57 +02:00
parent d3d0d1a75c
commit 8314dfaa1f
34 changed files with 1844 additions and 0 deletions

6
defaults/main.yml Normal file
View file

@ -0,0 +1,6 @@
---
mastodon_db_password: "{{ vault_mastodon_db_password }}"
redis_pass: "{{ vault_mastodon_redis_password }}"
mastodon_host: mastodon.example.com
use_http: true
mastodon_db_login_unix_socket: /var/run/postgresql

View file

@ -0,0 +1,19 @@
LOCAL_DOMAIN={{ local_domain }}
SINGLE_USER_MODE=false
SECRET_KEY_BASE={{ secret_key_base.stdout }}
OTP_SECRET={{ otp_secret.stdout }}
VAPID_PRIVATE_KEY={{ vapid_private_key.stdout }}
VAPID_PUBLIC_KEY={{ vapid_public_key.stdout }}
DB_HOST={{ db_host }}
DB_PORT={{ mastodon_db_port }}
DB_NAME={{ mastodon_db }}
DB_USER={{ mastodon_db_user }}
DB_PASS={{ mastodon_db_password }}
REDIS_HOST={{ redis_host }}
REDIS_PORT={{ redis_port }}
REDIS_PASSWORD={{ redis_pass }}
SMTP_SERVER=localhost
SMTP_PORT=25
SMTP_AUTH_METHOD=none
SMTP_OPENSSL_VERIFY_MODE=none
SMTP_FROM_ADDRESS=mastodon@{{ local_domain }}

View file

@ -0,0 +1,8 @@
# This starts a simple nginx for the letsencrypt acme challenge
server {
listen 80;
listen [::]:80;
server_name {{ mastodon_host }};
root {{ mastodon_home }}/{{ mastodon_path }}/public;
location /.well-known/acme-challenge/ { allow all; }
}

View file

@ -0,0 +1,196 @@
map $http_upgrade $connection_upgrade {
default upgrade;
'' close;
}
server {
{% if nginx_catch_all != "true" %}
listen 80;
listen [::]:80;
server_name {{ mastodon_host }};
{% endif %}
{% if nginx_catch_all == "true" %}
listen 80 default_server;
server_name _;
{% endif %}
# Useful for Let's Encrypt
location /.well-known/acme-challenge/ {
alias {{ mastodon_home }}/{{ mastodon_path }}/public/.well-known/acme-challenge/;
}
{% if use_http != "true" %}
location / { return 301 https://$host$request_uri; }
{% endif %}
{% if use_http == "true" %}
keepalive_timeout 70;
sendfile on;
client_max_body_size 8m;
root {{ mastodon_nginx_symlink }};
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;
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 /sw.js {
add_header Cache-Control "public, max-age=0";
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;
proxy_set_header Connection $connection_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;
proxy_set_header Connection $connection_upgrade;
tcp_nodelay on;
}
error_page 500 501 502 503 504 /500.html;
{% endif %}
}
{% if use_http != "true" %}
server {
{% if nginx_catch_all == "true" %}
listen 443 ssl http2 default_server;
server_name _;
{% endif %}
{% if nginx_catch_all != "true" %}
listen 443 ssl http2;
listen [::]:443 ssl http2;
server_name {{ mastodon_host }};
{% endif %}
ssl_protocols TLSv1.2;
ssl_ciphers HIGH:!MEDIUM:!LOW:!aNULL:!NULL:!SHA;
ssl_prefer_server_ciphers on;
ssl_session_cache shared:SSL:10m;
{% if disable_letsencrypt != "true" %}
ssl_certificate /etc/letsencrypt/live/{{ mastodon_host }}/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/{{ mastodon_host }}/privkey.pem;
{% endif %}
{% if disable_letsencrypt == "true" %}
ssl_certificate {{ self_signed_cert_location }}/server.crt;
ssl_certificate_key {{ self_signed_key_location }}/server.key;
{% endif %}
keepalive_timeout 70;
sendfile on;
client_max_body_size 8m;
root {{ mastodon_nginx_symlink }};
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;
{% if disable_hsts == "true" %}
add_header Strict-Transport-Security "max-age=31536000";
{% endif %}
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 /sw.js {
add_header Cache-Control "public, max-age=0";
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;
proxy_set_header Connection $connection_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;
proxy_set_header Connection $connection_upgrade;
tcp_nodelay on;
}
error_page 500 501 502 503 504 /500.html;
}
{% endif %}

View file

@ -0,0 +1,53 @@
[Unit]
Description=mastodon-sidekiq
After=network.target
[Service]
Type=simple
User={{ mastodon_user }}
WorkingDirectory={{ mastodon_home }}/{{ mastodon_path }}
Environment="RAILS_ENV=production"
Environment="DB_POOL=25"
Environment="MALLOC_ARENA_MAX=2"
Environment="LD_PRELOAD=libjemalloc.so"
ExecStart={{ mastodon_home }}/.rbenv/shims/bundle exec sidekiq -c 25
TimeoutSec=15
Restart=always
# Proc filesystem
ProcSubset=pid
ProtectProc=invisible
# Capabilities
CapabilityBoundingSet=
# Security
NoNewPrivileges=true
# Sandboxing
ProtectSystem=strict
PrivateTmp=true
PrivateDevices=true
PrivateUsers=true
ProtectHostname=true
ProtectKernelLogs=true
ProtectKernelModules=true
ProtectKernelTunables=true
ProtectControlGroups=true
RestrictAddressFamilies=AF_INET
RestrictAddressFamilies=AF_INET6
RestrictAddressFamilies=AF_NETLINK
RestrictAddressFamilies=AF_UNIX
RestrictNamespaces=true
LockPersonality=true
RestrictRealtime=true
RestrictSUIDSGID=true
RemoveIPC=true
PrivateMounts=true
ProtectClock=true
# System Call Filtering
SystemCallArchitectures=native
SystemCallFilter=~@cpu-emulation @debug @keyring @ipc @mount @obsolete @privileged @setuid
SystemCallFilter=@chown
SystemCallFilter=pipe
SystemCallFilter=pipe2
ReadWritePaths={{ mastodon_home }}/{{ mastodon_path }}
[Install]
WantedBy=multi-user.target

View file

@ -0,0 +1,51 @@
[Unit]
Description=mastodon-streaming
After=network.target
[Service]
Type=simple
User={{ mastodon_user }}
WorkingDirectory={{ mastodon_home }}/{{ mastodon_path }}
Environment="NODE_ENV=production"
Environment="PORT=4000"
Environment="STREAMING_CLUSTER_NUM=1"
ExecStart=/usr/bin/node ./streaming
TimeoutSec=15
Restart=always
# Proc filesystem
ProcSubset=pid
ProtectProc=invisible
# Capabilities
CapabilityBoundingSet=
# Security
NoNewPrivileges=true
# Sandboxing
ProtectSystem=strict
PrivateTmp=true
PrivateDevices=true
PrivateUsers=true
ProtectHostname=true
ProtectKernelLogs=true
ProtectKernelModules=true
ProtectKernelTunables=true
ProtectControlGroups=true
RestrictAddressFamilies=AF_INET
RestrictAddressFamilies=AF_INET6
RestrictAddressFamilies=AF_NETLINK
RestrictAddressFamilies=AF_UNIX
RestrictNamespaces=true
LockPersonality=true
RestrictRealtime=true
RestrictSUIDSGID=true
RemoveIPC=true
PrivateMounts=true
ProtectClock=true
# System Call Filtering
SystemCallArchitectures=native
SystemCallFilter=~@cpu-emulation @debug @keyring @ipc @memlock @mount @obsolete @privileged @resources @setuid
SystemCallFilter=pipe
SystemCallFilter=pipe2
ReadWritePaths={{ mastodon_home }}/{{ mastodon_path }}
[Install]
WantedBy=multi-user.target

View file

@ -0,0 +1,53 @@
[Unit]
Description=mastodon-web
After=network.target
[Service]
Type=simple
User={{ mastodon_user }}
WorkingDirectory={{ mastodon_home }}/{{ mastodon_path }}
Environment="RAILS_ENV=production"
Environment="PORT=3000"
Environment="LD_PRELOAD=libjemalloc.so"
ExecStart={{ mastodon_home }}/.rbenv/shims/bundle exec puma -C config/puma.rb
ExecReload=/bin/kill -SIGUSR1 $MAINPID
TimeoutSec=15
Restart=always
# Proc filesystem
ProcSubset=pid
ProtectProc=invisible
# Capabilities
CapabilityBoundingSet=
# Security
NoNewPrivileges=true
# Sandboxing
ProtectSystem=strict
PrivateTmp=true
PrivateDevices=true
PrivateUsers=true
ProtectHostname=true
ProtectKernelLogs=true
ProtectKernelModules=true
ProtectKernelTunables=true
ProtectControlGroups=true
RestrictAddressFamilies=AF_INET
RestrictAddressFamilies=AF_INET6
RestrictAddressFamilies=AF_NETLINK
RestrictAddressFamilies=AF_UNIX
RestrictNamespaces=true
LockPersonality=true
RestrictRealtime=true
RestrictSUIDSGID=true
RemoveIPC=true
PrivateMounts=true
ProtectClock=true
# System Call Filtering
SystemCallArchitectures=native
SystemCallFilter=~@cpu-emulation @debug @keyring @ipc @mount @obsolete @privileged @setuid
SystemCallFilter=@chown
SystemCallFilter=pipe
SystemCallFilter=pipe2
ReadWritePaths={{ mastodon_home }}/{{ mastodon_path }}
[Install]
WantedBy=multi-user.target

3
handlers/main.yml Normal file
View file

@ -0,0 +1,3 @@
---
# - name: Refresh aliases
# shell: newaliases

View file

@ -0,0 +1,2 @@
install_date: Sun Nov 6 12:31:50 2022
version: ''

22
meta/main.yml Normal file
View file

@ -0,0 +1,22 @@
---
galaxy_info:
author: Antonio J. Delgado (ajdelgado)
description: TODO - Description
# issue_tracker_url: https://github.com/uoi-io/ansible-galera/issues
license: GPLv3
min_ansible_version: "2.0"
github_branch: master
platforms:
- name: Ubuntu
versions:
- focal
- jammy
galaxy_tags: []# TODO
dependencies: []

54
tasks/bare.yml Normal file
View file

@ -0,0 +1,54 @@
---
- include_tasks: bare/init.yml
- include_tasks: bare/preflight-checks.yml
when: run_preflight_checks | bool
- include_tasks: bare/postgresql_packages.yml
- include_tasks: bare/postgresql_databases.yml
- include_tasks: bare/redis.yml
- include_tasks: bare/web_user.yml
- include_tasks: bare/web_repositories.yml
- include_tasks: bare/web_nodejs.yml
- include_tasks: bare/web_packages.yml
#RHEL uses firewall-cmd
- include_tasks: bare/web_ufw.yml
when:
- ansible_os_family == "Debian"
- include_tasks: bare/web_firewall-cmd.yml
when:
- ansible_os_family == "RedHat"
- include_tasks: bare/web_mastodon-preflight.yml
args:
apply:
become: true
become_user: mastodon
- include_tasks: bare/web_ruby.yml
args:
apply:
become: true
become_user: mastodon
- include_tasks: bare/web_redis.yml
- include_tasks: bare/web_selfsigned-ssl.yml
when: disable_letsencrypt | bool
- include_tasks: bare/web_mastodon-postflight.yml
args:
apply:
become: true
become_user: mastodon
- include_tasks: bare/web_letsencrypt.yml
when: disable_letsencrypt | bool == false
- include_tasks: bare/web_nginx.yml

View file

@ -0,0 +1,29 @@
---
- name: "Start and enable FirewallD service"
become: true
#Workaround for "Interactive authentication required" issue
become_user: root
service: "name={{ item }} state=started enabled=yes"
with_items:
- firewalld
- name: Allow SSH, HTTP and HTTPS through the firewall
firewalld:
permanent: true
immediate: true
service: "{{ item }}"
state: enabled
with_items:
- http
- https
- ssh
- name: Add localhost lo interface to the trusted zone
firewalld:
permanent: true
immediate: true
interface: "{{ item }}"
zone: trusted
state: enabled
with_items:
- lo

34
tasks/bare/init.yml Normal file
View file

@ -0,0 +1,34 @@
---
- name: Check if Python is installed
raw: command -v python3
register: python_installed
ignore_errors: True
- name: Check if yum is installed
raw: command -v yum
register: yum_installed
when: python_installed is failed
ignore_errors: True
#This checks if running higher Redhat version than 7
- name: Check if dnf is installed
raw: command -v dnf
register: dnf_installed
when: python_installed is failed
ignore_errors: True
- name: Check if apt is installed
raw: command -v apt
register: apt_installed
when: python_installed is failed
ignore_errors: True
- name: Bootstrap Python on RHEL
raw: yum install -y python3
when: python_installed is failed and yum_installed is succeeded
become: true
- name: Bootstrap Python on Ubuntu Linux
raw: apt update && apt install -y python3
when: python_installed is failed and apt_installed is succeeded
become: true

View file

@ -0,0 +1,31 @@
---
- stat: path=/etc/letsencrypt/live/{{ mastodon_host }}/fullchain.pem
register: letsencrypt_cert
- name: Copy letsencrypt nginx config
template:
src: ../files/nginx/letsencrypt.conf.j2
dest: /etc/nginx/sites-available/mastodon.conf
when: not letsencrypt_cert.stat.exists
- name: Symlink enabled site
file:
src: "/etc/nginx/sites-available/mastodon.conf"
dest: "/etc/nginx/sites-enabled/mastodon.conf"
state: link
when: not letsencrypt_cert.stat.exists
- name: Reload nginx
command: "systemctl reload nginx"
- name: Install letsencrypt cert
command: letsencrypt certonly -n --webroot -d {{ mastodon_host }} -w {{ mastodon_home }}/{{ mastodon_path }}/public/ --email "webmaster@{{ mastodon_host }}" --agree-tos
when: not letsencrypt_cert.stat.exists
- name: Letsencrypt Job
cron:
name: "letsencrypt renew"
minute: "15"
hour: "0"
job: "letsencrypt renew && service nginx reload"

View file

@ -0,0 +1,145 @@
- name: Bundle install
shell: "~/.rbenv/shims/bundle config set --local deployment 'true' && ~/.rbenv/shims/bundle config set --local without 'test' && ~/.rbenv/shims/bundle config set --local with 'development' && ~/.rbenv/shims/bundle install -j$(getconf _NPROCESSORS_ONLN)"
args:
chdir: "{{ mastodon_home }}/{{ mastodon_path }}"
- name: Yarn install
command: yarn install --pure-lockfile
args:
chdir: "{{ mastodon_home }}/{{ mastodon_path }}"
- name: Install systemd sidekiq Service Files
template:
src: ../files/systemd/mastodon-sidekiq.service.j2
dest: /etc/systemd/system/mastodon-sidekiq.service
become: true
become_user: root
- name: Install systemd web Service Files
template:
src: ../files/systemd/mastodon-web.service.j2
dest: /etc/systemd/system/mastodon-web.service
become: true
become_user: root
- name: Install systemd streaming Service Files
template:
src: ../files/systemd/mastodon-streaming.service.j2
dest: /etc/systemd/system/mastodon-streaming.service
become: true
become_user: root
- name: Media cleanup cronjob
cron:
name: "media cleanup"
minute: "15"
hour: "1"
job: '/bin/bash -c ''export PATH="$HOME/.rbenv/bin:$PATH"; eval "$(rbenv init -)"; cd {{ mastodon_home }}/{{ mastodon_path }} && RAILS_ENV=production ./bin/tootctl media remove'''
- stat: path={{ mastodon_home }}/{{ mastodon_path }}/.env.production
register: production_config
- name: Generate SECRET_KEY_BASE secret
shell: "RAILS_ENV=production ~/.rbenv/shims/bundle exec rake secret"
args:
chdir: "{{ mastodon_home }}/{{ mastodon_path }}"
register: secret_key_base
when: not production_config.stat.exists
- name: Generate OTP_SECRET secret
shell: "RAILS_ENV=production ~/.rbenv/shims/bundle exec rake secret"
args:
chdir: "{{ mastodon_home }}/{{ mastodon_path }}"
register: otp_secret
when: not production_config.stat.exists
- name: "Generate VAPID key pair into {{ mastodon_home }}/{{ mastodon_path }}/vapid.tmp"
shell: "RAILS_ENV=production ~/.rbenv/shims/bundle exec rake mastodon:webpush:generate_vapid_key > {{ mastodon_home }}/{{ mastodon_path }}/vapid.tmp | head -1 | cut -c 19-"
args:
chdir: "{{ mastodon_home }}/{{ mastodon_path }}"
when: not production_config.stat.exists
- name: Get VAPID_PRIVATE_KEY secret
shell: "cat {{ mastodon_home }}/{{ mastodon_path }}/vapid.tmp | head -1 | cut -c 19-"
register: vapid_private_key
when: not production_config.stat.exists
- name: Get VAPID_PUBLIC_KEY secret
shell: "cat {{ mastodon_home }}/{{ mastodon_path }}/vapid.tmp | tail -1 | cut -c 18-"
register: vapid_public_key
when: not production_config.stat.exists
- name: Ensure that the file used for vapid keypair generation is removed.
ansible.builtin.file:
path: "{{ mastodon_home }}/{{ mastodon_path }}/vapid.tmp"
state: absent
- name: Install Production env file
template:
src: files/mastodon/env.production.j2
dest: "{{ mastodon_home }}/{{ mastodon_path }}/.env.production"
when: not production_config.stat.exists
- name: Create database
shell: "RAILS_ENV=production ~/.rbenv/shims/bundle exec rails db:setup"
args:
chdir: "{{ mastodon_home }}/{{ mastodon_path }}"
environment:
SAFETY_ASSURED: 1
when: not production_config.stat.exists
- name: Migrate database
shell: "RAILS_ENV=production ~/.rbenv/shims/bundle exec rails db:migrate"
args:
chdir: "{{ mastodon_home }}/{{ mastodon_path }}"
when: production_config.stat.exists
- name: Ensure that we have correct file permissions with owner being the user and NGINX being the group
become: true
become_user: root
file:
path: "{{ mastodon_home }}/{{ mastodon_path }}"
owner: "{{ mastodon_user }}"
group: "nginx"
recurse: true
when:
- ansible_os_family == "RedHat"
- name: Ensure that we have correct file permissions with owner being the user and www-data being the group
become: true
become_user: root
file:
path: "{{ mastodon_home }}/{{ mastodon_path }}"
owner: "{{ mastodon_user }}"
group: "www-data"
recurse: true
when:
- ansible_os_family == "Debian"
#https://github.com/nodejs/node/issues/40455
#It's possible that this is a bug with ruby 3.0.3 and gets fixed with Mastodon 4.0.0
- name: Precompile assets with Legacy OpenSSL provider for RHEL9
shell: "NODE_OPTIONS=--openssl-legacy-provider RAILS_ENV=production ~/.rbenv/shims/bundle exec rails assets:precompile"
args:
chdir: "{{ mastodon_home }}/{{ mastodon_path }}"
when:
- ansible_os_family == "RedHat"
- ansible_facts['distribution_major_version'] == "9"
- name: Precompile assets
shell: "RAILS_ENV=production ~/.rbenv/shims/bundle exec rails assets:precompile"
args:
chdir: "{{ mastodon_home }}/{{ mastodon_path }}"
when: not (ansible_os_family == "RedHat" and ansible_facts['distribution_major_version'] == "9")
#We are installing new .env file, checking if .env file exists no longer required
# when: production_config.stat.exists
- name: "Start and enable Mastodon services"
become: true
#Workaround for "Interactive authentication required" issue
become_user: root
service: "name={{ item }} state=started enabled=yes"
with_items:
- mastodon-web.service
- mastodon-streaming.service
- mastodon-sidekiq.service

View file

@ -0,0 +1,45 @@
#We need different vars as register always fires off even when skipped
#This creates a mess with duplicate tasks that do different things, but its required
- name: Fetch latest stable Mastodon version number
shell: "git -c 'versionsort.suffix=-' ls-remote --tags --sort='v:refname' https://github.com/mastodon/mastodon.git | grep -v 'rc' | tail --lines=1 | cut -d '/' -f 3"
when:
- mastodon_version == "latest"
- mastodon_allow_prerelease | bool == false
register: latest_mastodon_tag
- name: Fetch latest stable Mastodon version number allowing release candidates
shell: "git -c 'versionsort.suffix=-' ls-remote --tags --sort='v:refname' https://github.com/mastodon/mastodon.git | tail --lines=1 | cut -d '/' -f 3"
when:
- mastodon_version == "latest"
- mastodon_allow_prerelease | bool
register: latest_mastodon_tag_rc
- name: Clone Specific version of Mastodon
git:
repo: "https://github.com/mastodon/mastodon.git"
dest: "{{ mastodon_home }}/{{mastodon_path}}"
clone: true
version: "{{ mastodon_version }}"
when: mastodon_version != "latest"
- name: Clone Latest Mastodon with allowed prerelease versions
git:
repo: "https://github.com/mastodon/mastodon.git"
dest: "{{ mastodon_home }}/{{mastodon_path}}"
clone: true
version: "{{ latest_mastodon_tag_rc.stdout }}"
when:
- mastodon_version == "latest"
- mastodon_allow_prerelease | bool
- name: Clone Latest Mastodon
git:
repo: "https://github.com/mastodon/mastodon.git"
dest: "{{ mastodon_home }}/{{mastodon_path}}"
clone: true
version: "{{ latest_mastodon_tag.stdout }}"
when:
- mastodon_version == "latest"
- mastodon_allow_prerelease | bool == false

188
tasks/bare/nginx.yml Normal file
View file

@ -0,0 +1,188 @@
---
#We need to enable the service first or we will not have the appropriate folders generated.
- name: "Start and enable NGINX service"
become: true
#Workaround for "Interactive authentication required" issue
become_user: root
service: "name={{ item }} state=started enabled=yes"
with_items:
- nginx
- name: "Set NGINX to run under {{ mastodon_user }} to avoid permission issues"
become: true
lineinfile:
dest: "/etc/nginx/nginx.conf"
regexp: "ˆuser"
line: "user mastodon;"
state: present
- name: "Ensure that NGINX doesn't run under the user nginx"
become: true
lineinfile:
dest: "/etc/nginx/nginx.conf"
regexp: "user.nginx;"
line: "user nginx;"
state: absent
when:
- ansible_os_family == "RedHat"
- name: "Ensure that NGINX doesn't run under the user www-data"
become: true
lineinfile:
dest: "/etc/nginx/nginx.conf"
regexp: "user.www-data;"
line: "user www-data;"
state: absent
when:
- ansible_os_family == "Debian"
- name: Copy nginx config
template:
src: ../files/nginx/mastodon.conf.j2
dest: /etc/nginx/sites-available/mastodon.conf
when:
- ansible_os_family == "Debian"
- mastodon_host is defined
- name: Symlink enabled site
file:
src: "/etc/nginx/sites-available/mastodon.conf"
dest: "/etc/nginx/sites-enabled/mastodon.conf"
state: link
when:
- ansible_os_family == "Debian"
- mastodon_host is defined
- name: Copy nginx config with RHEL folder stucture
template:
src: ../files/nginx/mastodon.conf.j2
dest: /etc/nginx/conf.d/mastodon.conf
when:
- ansible_os_family == "RedHat"
- mastodon_host is defined
- name: Create folder structure for Mastodon public folder
file:
path: "{{ mastodon_nginx_symlink }}"
state: directory
owner: "{{ mastodon_user }}"
group: "nginx"
recurse: true
when:
- ansible_os_family == "RedHat"
- mastodon_host is defined
- name: Create folder structure for Mastodon public folder
file:
path: "{{ mastodon_nginx_symlink }}"
state: directory
owner: "{{ mastodon_user }}"
group: "www-data"
recurse: true
when:
- ansible_os_family == "Debian"
- mastodon_host is defined
- name: Create a symbolic link of Mastodon public folder to comply with SELinux policy
become: true
file:
src: "{{ mastodon_home }}/{{ mastodon_path }}/public"
dest: "{{ mastodon_nginx_symlink }}"
state: link
owner: "{{ mastodon_user }}"
group: "nginx"
force: true
when:
- ansible_os_family == "RedHat"
- mastodon_host is defined
- name: Create a symbolic link of Mastodon public folder
become: true
file:
src: "{{ mastodon_home }}/{{ mastodon_path }}/public"
dest: "{{ mastodon_nginx_symlink }}"
state: link
owner: "{{ mastodon_user }}"
group: "www-data"
force: true
when:
- ansible_os_family == "Debian"
- mastodon_host is defined
- name: Permit NGINX SELinux permission to access filesystem
become: true
shell: "setsebool -P httpd_read_user_content 1"
when:
- ansible_os_family == "RedHat"
- mastodon_host is defined
- name: Permit SELinux permission to allow NGINX to make proxy connections with httpd_can_network_connect
become: true
shell: "setsebool -P httpd_can_network_connect 1"
when:
- ansible_os_family == "RedHat"
- mastodon_host is defined
- name: Permit SELinux permission to allow NGINX to make proxy connections with httpd_can_network_relay
become: true
shell: "setsebool -P httpd_can_network_relay 1"
when:
- ansible_os_family == "RedHat"
- mastodon_host is defined
#Reading and writing into users home directories as a web server or executing any binary as systemd service is
#really pretty anomalous behaviour. SELinux is completely right to flag this as it looks like we're an attacker.
#Potential security issue?
- name: Permit SELinux permission to allow NGINX to read contents of home folders (Required for Mastodon)
become: true
shell: "setsebool -P httpd_enable_homedirs on"
when:
- ansible_os_family == "RedHat"
- mastodon_host is defined
- name: Change SELinux properties of Mastodon symlink
become: true
shell: "chcon -Rt httpd_sys_content_t {{ mastodon_nginx_symlink }}"
when:
- ansible_os_family == "RedHat"
- mastodon_host is defined
- name: "Ensure that we have correct file permissions for /var/lib/nginx/ as we are not running NGINX under default user"
become: true
become_user: root
file:
path: "/var/lib/nginx/"
owner: "{{ mastodon_user }}"
group: "nginx"
recurse: true
when:
- ansible_os_family == "RedHat"
- name: "Ensure that we have correct file permissions for /var/lib/nginx/ as we are not running NGINX under default user"
become: true
become_user: root
file:
path: "/var/lib/nginx/"
owner: "{{ mastodon_user }}"
group: "www-data"
recurse: true
when:
- ansible_os_family == "Debian"
- name: Restart nginx
become: true
#Workaround for "Interactive authentication required" issue
become_user: root
service: name=nginx state=restarted
tags:
- systemd
- name: Check if Mastodon instance is up and running
uri:
url: 'https://{{ mastodon_host }}/about'
validate_certs: no
register: result
until: 'result.status == 200'
retries: 5
delay: 5

48
tasks/bare/nodejs.yml Normal file
View file

@ -0,0 +1,48 @@
#Ansible is not able to work with "dnf module" outside of installing them.
#Shell has to be used to check if a specific app stream is enabled, and then disable and enable
#the appropriate app streams to get the correct nodejs version for Yarn.
- name: Check if NodeJS 10 module is enabled
become: true
shell: "dnf module list nodejs | grep -q 'nodejs 10 \\[d\\]\\[e\\]' && echo true || echo false"
register: is_node10_enabled
ignore_errors: true
when:
- ansible_os_family == "RedHat"
- ansible_facts['distribution_major_version'] == "8"
- name: Disable NodeJS 10 module
become: true
shell: "dnf module disable nodejs:10 -y"
ignore_errors: true
when:
- ansible_os_family == "RedHat"
- ansible_facts['distribution_major_version'] == "8"
- is_node10_enabled.stdout | bool
- name: Enable NodeJS 16 module
become: true
shell: "dnf module enable nodejs:16 -y"
ignore_errors: true
when:
- ansible_os_family == "RedHat"
- ansible_facts['distribution_major_version'] == "8"
- is_node10_enabled.stdout | bool
- name: Install NodeJS 16 via DNF
become: true
dnf:
name: "@nodejs:16"
state: present
when:
- ansible_os_family == "RedHat"
- ansible_facts['distribution_major_version'] == "8"
#RHEL9 already installs NodeJS 16 by default
- name: Install NodeJS via DNF
become: true
dnf:
name: "nodejs"
state: present
when:
- ansible_os_family == "RedHat"
- ansible_facts['distribution_major_version'] == "9"

32
tasks/bare/packages.yml Normal file
View file

@ -0,0 +1,32 @@
---
#Speeds up the provisioning process as cache is not updated for every single package.
- name: Update package manager cache before installing packages.
become: true
package:
update_cache: true
- name: Install packages
package:
name: "{{ packages | map(attribute='package') }}"
update_cache: no
#This makes the package module non-OS generic
#https://docs.ansible.com/ansible/latest/collections/ansible/builtin/package_module.html
# state: latest
# install_recommends: no
#RHEL and Debian already has NodeJS in /usr/bin/node
#Unclear what this is supposed to fix
#- name: nodejs alternative
# alternatives:
# name: node
# link: /usr/bin/node
# path: /usr/bin/nodejs
# when: ansible_os_family == "Debian"
- name: Install Perl for building OpenSSL 1.1
become: true
package:
name: "perl"
state: present
when: (ansible_os_family == "RedHat" and ansible_facts['distribution_major_version'] == "9") or (ansible_facts['distribution'] == 'Ubuntu' and ansible_facts['distribution_release'] == 'jammy')

View file

@ -0,0 +1,74 @@
#Debian PostgreSQL installation automatically calls initdb i.e. it initializes the cluster with default encoding and locale.
#https://wiki.debian.org/PostgreSql
#RHEL requires manual initialisation
- name: "Find out if PostgreSQL is initialized"
stat:
path: "/var/lib/pgsql/data/pg_hba.conf"
register: postgres_data
when:
- ansible_os_family == "RedHat"
- name: "Initialize PostgreSQL"
shell: "postgresql-setup initdb"
when:
- ansible_os_family == "RedHat"
- not postgres_data.stat.exists
- name: "Start and enable services"
become: true
#Workaround for "Interactive authentication required" issue
become_user: root
service: "name={{ item }} state=started enabled=yes"
with_items:
- postgresql
- name: Create remote database {{ mastodon_db }}
postgresql_db:
name: "{{ mastodon_db }}"
login_host: "{{ mastodon_db_login_host }}"
login_password: "{{ mastodon_db_login_password }}"
login_user: "{{ mastodon_db_login_user }}"
port: "{{ mastodon_db_port }}"
register: create_remote_db
when:
- mastodon_db_login_user is defined
- mastodon_db_login_host is defined
- mastodon_db_login_password is defined
- mastodon_db_port is defined
- name: Create remote database user {{ mastodon_db_user }}
postgresql_user:
db: "{{ mastodon_db }}"
name: "{{ mastodon_db_user }}"
password: "{{ mastodon_db_password }}"
login_host: "{{ mastodon_db_login_host }}"
login_password: "{{ mastodon_db_login_password }}"
login_user: "{{ mastodon_db_login_user }}"
port: "{{ mastodon_db_port }}"
role_attr_flags: CREATEDB
register: create_remote_db_user
when:
- mastodon_db_login_user is defined
- mastodon_db_login_host is defined
- mastodon_db_login_password is defined
- mastodon_db_port is defined
- name: Create database {{ mastodon_db }}
become: true
postgresql_db:
name: "{{ mastodon_db }}"
login_unix_socket: "{{ mastodon_db_login_unix_socket }}"
register: create_local_db
when: create_remote_db is skipped
- name: Create database user {{ mastodon_db_user }}
become: true
postgresql_user:
db: "{{ mastodon_db }}"
name: "{{ mastodon_db_user }}"
password: "{{ mastodon_db_password }}"
encrypted: true
login_unix_socket: "{{ mastodon_db_login_unix_socket }}"
role_attr_flags: CREATEDB
when: create_remote_db_user is skipped

View file

@ -0,0 +1,19 @@
---
#Speeds up the provisioning process as cache is not updated for every single package.
- name: Update package manager cache before installing packages.
become: true
package:
update_cache: true
- name: Install Postgres packages
become: true
package:
name: "{{ item.package }}"
update_cache: no
#This makes the package module non-OS generic
#https://docs.ansible.com/ansible/latest/collections/ansible/builtin/package_module.html
# cache_valid_time: 3600
# state: latest
# install_recommends: no
with_items: "{{ postgres.packages }}"

View file

@ -0,0 +1,183 @@
---
#We need different vars as register always fires off even when skipped
#This creates a mess with duplicate tasks that do different things, but its required
#Help wanted to clean up the preflight task
- name: Check if a Mastodon installation already exists
stat:
path: "{{ mastodon_home }}/{{mastodon_path}}"
register: mastodon_install_exists
- name: Verify that existing Mastodon installation is a valid git folder
stat:
path: "{{ mastodon_home }}/{{mastodon_path}}/.git"
register: mastodon_is_git
- name: Verify if upgrade folder is valid
fail:
msg:
- "ERROR: A folder defined for Mastodon installation already exists but its not a valid git folder."
- "Halting playbook and bailing out to prevent a destructive operation."
- "If you think this is a mistake or you know what you're doing, set the run_preflight_checks variable to false"
when:
- mastodon_install_exists.stat.exists
- not mastodon_is_git.stat.exists
#Have to run it under Mastodon user due of Git Security changes in newer OSes
#https://github.blog/2022-04-12-git-security-vulnerability-announced/
- name: Get local major version
shell: "git tag --points-at HEAD | cut -c2-2"
args:
chdir: "{{ mastodon_home }}/{{ mastodon_path }}"
when:
- mastodon_install_exists.stat.exists
- mastodon_is_git.stat.exists
become: true
become_user: mastodon
register: local_major_ver
#Have to run it under Mastodon user due of Git Security changes in newer OSes
#https://github.blog/2022-04-12-git-security-vulnerability-announced/
- name: Get local minor version
shell: "git tag --points-at HEAD | cut -c4-4"
args:
chdir: "{{ mastodon_home }}/{{ mastodon_path }}"
when:
- mastodon_install_exists.stat.exists
- mastodon_is_git.stat.exists
register: local_minor_ver
become: true
become_user: mastodon
- name: Fetch latest stable major Mastodon version number
shell: "git -c 'versionsort.suffix=-' ls-remote --tags --sort='v:refname' https://github.com/mastodon/mastodon.git | grep -v 'rc' | tail --lines=1 | cut -d '/' -f 3 | cut -c2-2"
when:
- mastodon_version == "latest"
- mastodon_allow_prerelease | bool == false
- mastodon_is_git.stat.exists
- mastodon_install_exists.stat.exists
register: latest_mastodon_tag_major
- name: Fetch latest stable major Mastodon version number allowing release candidates
shell: "git -c 'versionsort.suffix=-' ls-remote --tags --sort='v:refname' https://github.com/mastodon/mastodon.git | tail --lines=1 | cut -d '/' -f 3 | cut -c2-2"
when:
- mastodon_version == "latest"
- mastodon_allow_prerelease | bool
- mastodon_is_git.stat.exists
- mastodon_install_exists.stat.exists
register: latest_mastodon_tag_rc_major
- name: Fetch latest minor stable Mastodon version number
shell: "git -c 'versionsort.suffix=-' ls-remote --tags --sort='v:refname' https://github.com/mastodon/mastodon.git | grep -v 'rc' | tail --lines=1 | cut -d '/' -f 3 | cut -c4-4"
when:
- mastodon_version == "latest"
- mastodon_allow_prerelease | bool == false
- mastodon_is_git.stat.exists
- mastodon_install_exists.stat.exists
register: latest_mastodon_tag_minor
- name: Fetch latest minor stable Mastodon version number allowing release candidates
shell: "git -c 'versionsort.suffix=-' ls-remote --tags --sort='v:refname' https://github.com/mastodon/mastodon.git | tail --lines=1 | cut -d '/' -f 3 | cut -c4-4"
when:
- mastodon_version == "latest"
- mastodon_allow_prerelease | bool
- mastodon_is_git.stat.exists
- mastodon_install_exists.stat.exists
register: latest_mastodon_tag_rc_minor
- name: Fetch specified major Mastodon version number
shell: "echo '{{ mastodon_version }}' | cut -c2-2"
when:
- mastodon_version != "latest"
- mastodon_is_git.stat.exists
- mastodon_install_exists.stat.exists
register: specific_mastodon_tag_major
- name: Fetch specified minor Mastodon version number
shell: "echo '{{ mastodon_version }}' | cut -c4-4"
when:
- mastodon_version != "latest"
- mastodon_is_git.stat.exists
- mastodon_install_exists.stat.exists
register: specific_mastodon_tag_minor
- name: Verify MAJOR upgrade path for specific version of Mastodon
fail:
msg:
- "ERROR: You are attempting to perform a MAJOR version upgrade that is not supported for automation!"
- "It is HEAVILY recommended to do the upgrade by hand by following the upgrade instructions listed in the Mastodon release notes!"
- "Halting playbook and bailing out to prevent a destructive operation."
- "If you think this is a mistake or you know what you're doing, set the run_preflight_checks variable to false"
when:
- mastodon_version != "latest"
- mastodon_is_git.stat.exists
- mastodon_install_exists.stat.exists
- local_major_ver.stdout != specific_mastodon_tag_major.stdout
- name: Verify MAJOR upgrade path for Latest Mastodon with allowed prerelease versions
fail:
msg:
- "ERROR: You are attempting to perform a MAJOR version upgrade that is not supported for automation!"
- "It is HEAVILY recommended to do the upgrade by hand by following the upgrade instructions listed in the Mastodon release notes!"
- "Halting playbook and bailing out to prevent a destructive operation."
- "If you think this is a mistake or you know what you're doing, set the run_preflight_checks variable to false"
when:
- mastodon_version == "latest"
- mastodon_allow_prerelease | bool
- mastodon_install_exists.stat.exists
- local_major_ver.stdout != latest_mastodon_tag_rc_major.stdout
- name: Verify MAJOR upgrade path for Latest Mastodon
fail:
msg:
- "ERROR: You are attempting to perform a MAJOR version upgrade that is not supported for automation!"
- "It is HEAVILY recommended to do the upgrade by hand by following the upgrade instructions listed in the Mastodon release notes!"
- "Halting playbook and bailing out to prevent a destructive operation."
- "If you think this is a mistake or you know what you're doing, set the run_preflight_checks variable to false"
when:
- mastodon_version == "latest"
- mastodon_allow_prerelease | bool == false
- mastodon_install_exists.stat.exists
- mastodon_is_git.stat.exists
- local_major_ver.stdout != latest_mastodon_tag_major.stdout
- name: Verify MINOR upgrade path for specific version of Mastodon
fail:
msg:
- "ERROR: You are attempting to perform a MINOR version upgrade that is not recommended to be upgraded by automation!"
- "It is HEAVILY recommended to do the upgrade by hand by following the upgrade instructions listed in the Mastodon release notes!"
- "Halting playbook and bailing out to prevent a destructive operation."
- "If you think this is a mistake or you know what you're doing, set the run_preflight_checks variable to false"
when:
- mastodon_version != "latest"
- mastodon_is_git.stat.exists
- mastodon_install_exists.stat.exists
- local_minor_ver.stdout != specific_mastodon_tag_minor.stdout
- name: Verify MAJOR upgrade path for Latest Mastodon with allowed prerelease versions
fail:
msg:
- "ERROR: You are attempting to perform a MINOR version upgrade that is not recommended to be upgraded by automation!"
- "It is HEAVILY recommended to do the upgrade by hand by following the upgrade instructions listed in the Mastodon release notes!"
- "Halting playbook and bailing out to prevent a destructive operation."
- "If you think this is a mistake or you know what you're doing, set the run_preflight_checks variable to false"
when:
- mastodon_version == "latest"
- mastodon_allow_prerelease | bool
- mastodon_is_git.stat.exists
- mastodon_install_exists.stat.exists
- local_minor_ver.stdout != latest_mastodon_tag_rc_minor.stdout
- name: Verify MINOR upgrade path for Latest Mastodon
fail:
msg:
- "ERROR: You are attempting to perform a MINOR version upgrade that is not recommended to be upgraded by automation!"
- "It is HEAVILY recommended to do the upgrade by hand by following the upgrade instructions listed in the Mastodon release notes!"
- "Halting playbook and bailing out to prevent a destructive operation."
- "If you think this is a mistake or you know what you're doing, set the run_preflight_checks variable to false"
when:
- mastodon_version == "latest"
- mastodon_allow_prerelease | bool == false
- mastodon_install_exists.stat.exists
- mastodon_is_git.stat.exists
- local_minor_ver.stdout != latest_mastodon_tag_minor.stdout

45
tasks/bare/redis.yml Normal file
View file

@ -0,0 +1,45 @@
- name: "Start and enable redis service"
become: true
#Workaround for "Interactive authentication required" issue
become_user: root
service: "name={{ item }} state=started enabled=yes"
with_items:
- redis
- name: Set Redis password for RHEL8 system
become: true
lineinfile:
dest: "/etc/redis.conf"
regexp: "ˆrequirepass"
line: "requirepass {{ redis_pass }}"
state: present
when:
- ansible_os_family == "RedHat"
- ansible_facts['distribution_major_version'] == "8"
- name: Set Redis password for RHEL9 system
become: true
lineinfile:
dest: "/etc/redis/redis.conf"
regexp: "ˆrequirepass"
line: "requirepass {{ redis_pass }}"
state: present
when:
- ansible_os_family == "RedHat"
- ansible_facts['distribution_major_version'] == "9"
- name: Set Redis password for Debian system
become: true
lineinfile:
dest: "/etc/redis/redis.conf"
regexp: "ˆrequirepass"
line: "requirepass {{ redis_pass }}"
state: present
when:
- ansible_os_family == "Debian"
- name: Restart Redis
become: true
#Workaround for "Interactive authentication required" issue
become_user: root
service: name=redis state=restarted

View file

@ -0,0 +1,71 @@
---
- name: Install required packages for HTTPS repositories
apt: name={{ item.package }} state=present update_cache=yes cache_valid_time=3600
become: true
with_items:
- package: apt-transport-https
- package: ca-certificates
when:
- ansible_os_family == "Debian"
- name: Install APT repository keys
apt_key: id={{ item.id }} url={{ item.url }} state=present
become: true
with_items:
- { id: "72ECF46A56B4AD39C907BBB71646B01B86E50310", url: "https://dl.yarnpkg.com/debian/pubkey.gpg" }
- { id: "9FD3B784BC1C6FC31A8A0A1C1655A0AB68576280", url: "https://deb.nodesource.com/gpgkey/nodesource.gpg.key" }
when:
- ansible_os_family == "Debian"
- name: Install APT repositories
apt_repository: repo={{ item.repo }} state=present
become: true
with_items:
- repo: "deb https://dl.yarnpkg.com/debian/ stable main"
- repo: "deb https://deb.nodesource.com/node_{{ node_major_version }}.x {{ ubuntu_codename }} main"
when:
- ansible_os_family == "Debian"
#This is meant for CentOS8/Rocky8/AlmaLinux8/Fedora
#On actual Redhat distro we need to execute subscription-manager repos --enable codeready-builder-for-rhel-8-x86_64-rpms
#Ansible is not able to work with "dnf config-manager" so have to execute as shell command. Ffmpeg and devel packages dependency
- name: Enable Powertools repository for RHEL8
become: true
shell: dnf config-manager --set-enabled powertools
when:
- ansible_os_family == "RedHat"
- ansible_facts['distribution_major_version'] == "8"
- name: Enable crb repository for RHEL9
become: true
shell: dnf config-manager --set-enabled crb
when:
- ansible_os_family == "RedHat"
- ansible_facts['distribution_major_version'] == "9"
- name: Install RPMFusion repository for RHEL
become: true
dnf:
name: "https://mirrors.rpmfusion.org/free/el/rpmfusion-free-release-{{ ansible_distribution_major_version }}.noarch.rpm"
#distribution-gpg-keys doesn't contain the up to date GPG keys for RPMFusion and even official RPMFusion install instructions
#specify to ignore GPG checks.
disable_gpg_check: true
state: present
when: ansible_os_family == "RedHat"
- name: Install Yarn repository for RHEL
become: true
get_url:
url: https://dl.yarnpkg.com/rpm/yarn.repo
dest: /etc/yum.repos.d/yarn.repo
mode: '0644'
when: ansible_os_family == "RedHat"
- name: Import the Yarn repository GPG key for RHEL
become: true
rpm_key:
state: present
key: https://dl.yarnpkg.com/rpm/pubkey.gpg
when: ansible_os_family == "RedHat"

81
tasks/bare/ruby.yml Normal file
View file

@ -0,0 +1,81 @@
---
- name: Fetch Ruby version required by Mastodon
shell: "cat {{ mastodon_home }}/{{ mastodon_path }}/.ruby-version"
register: ruby_version
- name: Fetch latest tagged release of rbenv
shell: "git -c 'versionsort.suffix=-' ls-remote --tags --sort='v:refname' https://github.com/rbenv/rbenv.git | tail --lines=1 | cut -d '/' -f 3"
register: rbenv_version
- name: Fetch latest tagged release of ruby-build
shell: "git -c 'versionsort.suffix=-' ls-remote --tags --sort='v:refname' https://github.com/rbenv/ruby-build.git | tail --lines=1 | cut -d '/' -f 3"
register: ruby_build_version
- name: "Clone rbenv version {{ rbenv_version.stdout }}"
git:
repo: "https://github.com/rbenv/rbenv.git"
dest: "~/.rbenv"
clone: true
version: "{{ rbenv_version.stdout }}"
- name: "Clone ruby-build version {{ ruby_build_version.stdout }}"
git:
repo: "https://github.com/rbenv/ruby-build.git"
dest: "~/.rbenv/plugins/ruby-build"
clone: true
version: "{{ ruby_build_version.stdout }}"
register: ruby_build
- name: Configure rbenv
command: ./configure
args:
chdir: "~/.rbenv/src"
register: rbenv_configure
- name: Build rbenv
command: make
args:
chdir: "~/.rbenv/src"
when: rbenv_configure is succeeded
- name: Update profile settings
copy:
dest: "~/.bashrc"
content: |
export PATH="~/.rbenv/bin:${PATH}"
eval "$(rbenv init -)"
- name: Check if the Ruby version is already installed
shell: "~/.rbenv/bin/rbenv versions | grep -q {{ ruby_version.stdout }}"
register: ruby_installed
ignore_errors: true
check_mode: no
- name: Install Ruby {{ ruby_version.stdout }}
shell: "~/.rbenv/bin/rbenv install {{ ruby_version.stdout }}"
args:
executable: /bin/bash
when: ruby_installed is failed
- name: Set the default Ruby version to {{ ruby_version.stdout }}
shell: "~/.rbenv/bin/rbenv global {{ ruby_version.stdout }}"
args:
executable: /bin/bash
register: default_ruby_version
#Locking the Bundler version by itself is no longer required
#https://bundler.io/blog/2022/01/23/bundler-v2-3.html
- name: Install bundler
shell: 'export PATH="$HOME/.rbenv/bin:$PATH"; eval "$(rbenv init -)"; gem install bundler'
args:
executable: /bin/bash
when: default_ruby_version is succeeded
- name: Set SELinux policy for rbenv bundle same as /bin
become: true
#Workaround for "SELinux policy is not managed or store cannot be accessed" issue
become_user: root
shell: "chcon -R --reference /bin {{ mastodon_home }}/.rbenv/shims/bundle"
when:
- ansible_os_family == "RedHat"

View file

@ -0,0 +1,20 @@
---
- name: Create folder location for self-signed SSL certs
become: true
file:
path: "{{ item }}"
state: directory
owner: root
group: root
recurse: true
loop:
- "{{ self_signed_cert_location }}"
- "{{ self_signed_key_location }}"
- name: Create self-signed certificate
become: true
shell: >
openssl req -x509 -nodes -subj '/CN={{ mastodon_host }}' -days 365
-newkey rsa:4096 -sha256 -keyout '{{ self_signed_key_location }}/server.key' -out '{{ self_signed_cert_location }}/server.crt'
creates='{{ self_signed_cert_location }}/server.crt'

22
tasks/bare/ufw.yml Normal file
View file

@ -0,0 +1,22 @@
---
- name: Allow ssh through firewall
ufw:
proto: tcp
port: "22"
rule: allow
- name: Set ufw policy
ufw:
state: enabled
direction: incoming
policy: deny
- name: Allow nginx firewall
ufw:
proto: tcp
port: "80"
rule: allow
- name: Allow nginx ssl firewall
ufw:
proto: tcp
port: "443"
rule: allow

7
tasks/bare/user.yml Normal file
View file

@ -0,0 +1,7 @@
- name: Create Mastodon user
user:
name: "{{ mastodon_user }}"
createhome: true
shell: /bin/bash
home: "{{ mastodon_home }}"

48
tasks/docker/core.yml Normal file
View file

@ -0,0 +1,48 @@
---
- import_tasks: ubuntu.yml
when: ansible_distribution == 'Ubuntu'
- name: Install docker-compose
get_url:
url: "https://github.com/docker/compose/releases/download/{{ docker_compose_version }}/docker-compose-Linux-x86_64"
dest: "/usr/bin/docker-compose"
sha256sum: "{{ docker_compose_hash }}"
mode: 0755
become: true
- name: Install Docker packages
package:
name: "{{ item.package }}"
state: latest
become: true
register: docker_packages
with_items: "{{ install_packages }}"
- name: Install Python packages
pip:
name: "{{ item.package }}"
state: latest
with_items: "{{ install_python_packages }}"
- name: Uninstall Python packages
pip:
name: "{{ item.package }}"
state: absent
with_items: "{{ uninstall_python_packages }}"
- name: Install docker-compose Python
pip:
name: docker-compose
version: 1.17.0rc1
- name: Check for existing network
raw: "docker network inspect {{ mastodon_docker_network }}"
register: docker_network
ignore_errors: True
become: true
- name: Create main docker network
raw: "docker network create -d bridge {{ mastodon_docker_network }}"
when: docker_network is failed
become: true

5
tasks/docker/docker.yml Normal file
View file

@ -0,0 +1,5 @@
- name: Bootstrapping
import_tasks: docker/init.yml
- import_tasks: docker/core.yml

26
tasks/docker/init.yml Normal file
View file

@ -0,0 +1,26 @@
---
- name: Check if Python is installed
raw: command -v python
register: python_installed
ignore_errors: True
- name: Check if yum is installed
raw: command -v yum
register: yum_installed
ignore_errors: True
- name: Check if apt is installed
raw: command -v apt
register: apt_installed
ignore_errors: True
- name: Bootstrap Python on Amazon Linux
raw: yum update && yum install -y python27
when: python_installed is failed and yum_installed is succeeded
become: true
- name: Bootstrap Python on Ubuntu Linux
raw: apt update && apt install -y python
when: python_installed is failed and apt_installed is succeeded
become: true

26
tasks/docker/ubuntu.yml Normal file
View file

@ -0,0 +1,26 @@
---
- name: Install required packages for HTTPS repositories
apt: name={{ item.package }} state=present update_cache=yes cache_valid_time=3600
become: true
with_items:
- { package: apt-transport-https }
- { package: ca-certificates }
- name: Add Docker repository for Ubuntu
apt_key: id={{ item.id }} url={{ item.url }} state=present
become: true
with_items:
- { id: "9DC858229FC7DD38854AE2D88D81803C0EBFCD88", url: "https://download.docker.com/linux/ubuntu/gpg" }
- name: Install Docker repository
apt_repository: repo={{ item.repo }} state=present
become: true
with_items:
- { repo: "deb [arch=amd64] https://download.docker.com/linux/ubuntu {{ ansible_distribution_release }} stable" }
- name: Run apt update
apt:
update_cache: true
cache_valid_time: 3600
become: true

8
tasks/main.yml Normal file
View file

@ -0,0 +1,8 @@
---
- name: Ensure bare metal installation
include_tasks: bare.yml
when: mastodon_bare_installation
- name: Ensure docker installation
include_tasks: docker.yml
when: not mastodon_bare_installation

190
templates/env.j2 Normal file
View file

@ -0,0 +1,190 @@
# Service dependencies
# You may set REDIS_URL instead for more advanced options
# You may also set REDIS_NAMESPACE to share Redis between multiple Mastodon servers
REDIS_HOST={{ redis_host }}
REDIS_PORT={{ redis_port }}
# You may set DATABASE_URL instead for more advanced options
DB_HOST={{ db_host }}
DB_USER={{ db_user }}
DB_NAME={{ db_name }}
DB_PASS={{ db_pass }}
DB_PORT={{ db_port }}
# Federation
# Note: Changing LOCAL_DOMAIN or LOCAL_HTTPS at a later time will cause unwanted side effects.
# LOCAL_DOMAIN should *NOT* contain the protocol part of the domain e.g https://example.com.
LOCAL_DOMAIN={{ local_domain }}
LOCAL_HTTPS={{ local_https }}
{% if web_domain %}
WEB_DOMAIN={{ web_domain }}
{% endif %}
{% if alternate_domains %}
ALTERNATE_DOMAINS={{ alternate_domains }}
{% endif %}
# Application secrets
# Generate each with the `RAILS_ENV=production bundle exec rake secret` task (`docker-compose run --rm web rake secret` if you use docker compose)
PAPERCLIP_SECRET={{ paperclip_secret }}
SECRET_KEY_BASE={{ secret_key_base }}
OTP_SECRET={{ otp_secret }}
# VAPID keys (used for push notifications
# You can generate the keys using the following command (first is the private key, second is the public one)
# You should only generate this once per instance. If you later decide to change it, all push subscription will
# be invalidated, requiring the users to access the website again to resubscribe.
#
# Generate with `RAILS_ENV=production bundle exec rake mastodon:webpush:generate_vapid_key` task (`docker-compose run --rm web rake mastodon:webpush:generate_vapid_key` if you use docker compose)
#
# For more information visit https://rossta.net/blog/using-the-web-push-api-with-vapid.html
VAPID_PRIVATE_KEY={{ vapid_private_key }}
VAPID_PUBLIC_KEY=({ vapid_public_key })
{% if single_user_mode %}
# Registrations
# Single user mode will disable registrations and redirect frontpage to the first profile
SINGLE_USER_MODE=true
{% endif %}
{% if email_domain_blacklist %}
# Prevent registrations with following e-mail domains
EMAIL_DOMAIN_BLACKLIST={{ email_domain_blacklist }}
{% endif %}
{% if email_domain_whitelist %}
# Only allow registrations with the following e-mail domains
EMAIL_DOMAIN_WHITELIST={{ email_domain_whitelist }}
{% endif %}
# Optionally change default language
DEFAULT_LOCALE={{ default_locale }}
# E-mail configuration
# Note: Mailgun and SparkPost (https://sparkpo.st/smtp) each have good free tiers
# If you want to use an SMTP server without authentication (e.g local Postfix relay)
# then set SMTP_AUTH_METHOD and SMTP_OPENSSL_VERIFY_MODE to 'none' and
# *comment* SMTP_LOGIN and SMTP_PASSWORD (leaving them blank is not enough).
SMTP_SERVER={{ smtp_server }}
SMTP_PORT={{ smtp_port }}
SMTP_LOGIN={{ smtp_login }}
SMTP_PASSWORD={{ smtp_password }}
SMTP_FROM_ADDRESS={{ smtp_from_address }}
{% if smtp_domain %}
SMTP_DOMAIN={{ smtp_domain }}
{% endif %}
{% if smtp_delivery_method %}
SMTP_DELIVERY_METHOD={{ smtp_delivery_method }}
{% endif %}
{% if smtp_auth_method %}
SMTP_AUTH_METHOD={{ smtp_auth_method }}
{% endif %}
{% smtp_ca_file %}
SMTP_CA_FILE={{ smtp_ca_file }}
{% endif %}
{% if smtp_openssl_verify_mode %}
SMTP_OPENSSL_VERIFY_MODE={{ smtp_openssl_verify_mode }}
{% endif %}
{% if smtp_enable_starttls_auto %}
SMTP_ENABLE_STARTTLS_AUTO={{ smtp_enable_starttls_auto }}
{% endif %}
{% if smtp_tls %}
SMTP_TLS={{ smtp_tls }}
{% endif %}
{% if paperclip_root_path %}
# Optional user upload path and URL (images, avatars). Default is :rails_root/public/system. If you set this variable, you are responsible for making your HTTP server (eg. nginx) serve these files.
PAPERCLIP_ROOT_PATH={{ paperclip_root_path }}
{% endif %}
{% if paperclip_root_url %}
PAPERCLIP_ROOT_URL={{ paperclip_root_url }}
{% endif %}
{% if cdn_host %}
# Optional asset host for multi-server setups
CDN_HOST={{ cdn_host }}
{% endif %}
{% if s3_enabled %}
S3_ENABLED={{ s3_enabled }}
{% endif %}
{% if s3_bucket %}
S3_BUCKET={{ s3_bucket }}
{% endif %}
{% if aws_access_key_id %}
AWS_ACCESS_KEY_ID={{ aws_access_key_id }}
{% endif %}
{% if aws_secret_access_key %}
AWS_SECRET_ACCESS_KEY={{ aws_secret_access_key }}
{% endif %}
{% if s3_region %}
S3_REGION={{ s3_region }}
{% endif %}
{% if s3_protocol %}
S3_PROTOCOL={{ s3_protocol }}
{% endif %}
{% if s3_hostname %}
S3_HOSTNAME={{ s3_hostname }}
{% endif %}
{% if s3_endpoint %}
S3_ENDPOINT={{ s3_endpoint }}
{% endif %}
{% if s3_signature_version %}
S3_SIGNATURE_VERSION={{ s3_signature_version }}
{% endif %}
{% if swift_enabled %}
SWIFT_ENABLED={{ swift_enabled }}
{% endif %}
{% if swift_username %}
SWIFT_USERNAME={{ swift_username }}
{% endif %}
{% if swift_tenant %}
# For Keystone V3, the value for SWIFT_TENANT should be the project name
SWIFT_TENANT=
{% endif %}
{% if swift_password %}
SWIFT_PASSWORD={{ swift_password }}
{% endif %}
{% if swift_auth_url %}
# Keystone V2 and V3 URLs are supported. Use a V3 URL if possible to avoid
# issues with token rate-limiting during high load.
SWIFT_AUTH_URL={{ swift_auth_url }}
{% endif %}
{% if swift_container %}
SWIFT_CONTAINER={{ swift_container }}
{% endif %}
{% if swift_object_url %}
SWIFT_OBJECT_URL={{ swift_object_url }}
{% endif %}
{% if swift_region %}
SWIFT_REGION={{ swift_region }}
{% endif %}
{% if swift_domain_name %}
# Defaults to 'default'
# SWIFT_DOMAIN_NAME={{ swift_domain_name }}
{% endif %}
{% if swift_cache_ttl %}
# Defaults to 60 seconds. Set to 0 to disable
# SWIFT_CACHE_TTL={{ swift_cache_ttl }}
{% endif %}
{% if s3_cloudfront_host %}
# Optional alias for S3 if you want to use Cloudfront or Cloudflare in front
S3_CLOUDFRONT_HOST={{ s3_cloudfront_host }}
{% endif %}
{% if streaming_api_base_url %}
# Streaming API integration
# STREAMING_API_BASE_URL={{ streaming_api_base_url }}
{% endif %}
{% if prepared_statements %}
# Advanced settings
# If you need to use pgBouncer, you need to disable prepared statements:
# PREPARED_STATEMENTS={{ prepared_statements }}
{% endif %}
{% if streaming_cluster_num %}
# Cluster number setting for streaming API server.
# If you comment out following line, cluster number will be `numOfCpuCores - 1`.
STREAMING_CLUSTER_NUM=1
{% endif %}