Moebooru

From Bibliotheca Anonoma

Setting Up Moebooru

Get configuration tips from here:

http://wiki.douglasqsantos.com.br/doku.php/deploying_a_rails_app_on_debian_jessie_with_capistrano_nginx_and_puma_en

Create Moebooru User

https://github.com/moebooru/moebooru

Create a specific non-login daemon user just for moebooru (Though it will have bash shell for setup purposes temporarily). Then create a systemd service for it.

sudo git clone https://github.com/moebooru/moebooru.git /var/www/booru.eikonos.org
sudo useradd -s /bin/bash -d /var/www/booru.eikonos.org -r moebooru
sudo chown -R moebooru:moebooru /var/www/booru.eikonos.org

Setup Postgresql

sudo rpm -ivh http://yum.postgresql.org/9.5/redhat/rhel-7-x86_64/pgdg-centos95-9.5-2.noarch.rpm
sudo yum install postgresql95 postgresql95-devel postgresql95-server libpqxx libpqxx-devel

libpqxx is required for libpq-ruby to build.

Now log in and create the moebooru user:

# su - postgres
$ psql
postgres# create user moebooru_user with password 'the_password' createdb;

Finally, edit /var/lib/pgsql/9.5/data/pg_hba.conf from ident to md5 (except for the UNIX socket line) to allow users to log in using a password (required by moebooru's config), it should look like the following:

# "local" is for Unix domain socket connections only
local   all             all                                     peer
# IPv4 local connections:
host    all             all             127.0.0.1/32            md5
# IPv6 local connections:
host    all             all             ::1/128                 md5
# Allow replication connections from localhost, by a user with the
# replication privilege.
#local   replication     postgres                                peer
#host    replication     postgres        127.0.0.1/32            trust
#host    replication     postgres        ::1/128                 trust

By default the PostgreSQL server uses "trust" authentication, but this just trusts any user on the system from superuser to normal user to access the SQL server, clearly a bad idea...

Once you have this file edited as seen above, restart postgresql.

sudo systemctl restart postgresql-9.5

Advisory: If this postgresql server is on the same machine, make sure the firewall is configured to prevent remote access to postgresql ports. Also ensure that SSH keys are used instead of passwords. If this postgresql server is on another machine in the network/internet, make sure moebooru is connecting via SSL.

Install Nodejs

NodeJS is necessary for the frontend. You should obtain the latest version, 6.x:

Run this script as root:

curl --silent --location https://rpm.nodesource.com/setup_6.x | bash -

https://nodejs.org/en/download/package-manager/

Setup Ruby

Set up Normal Ruby 2.3 on CentOS/Red Hat

https://www.softwarecollections.org/en/scls/rhscl/rh-ruby23/

Set up RVM (Rubinius or JRuby)

For RVM, just install any ol' ruby 2.x, we won't be using it after the compilation stage. Also install all the build dependencies.

sudo yum install ruby
sudo yum install -y patch autoconf patch automake libtool bison sqlite-devel

One special build dependency is llvm-3.6+. Unfortunately, CentOS 7's EPEL repo only has 3.4, so we will need to get the latest build of LLVM from Fedora COPR.

/etc/yum.repos.d/daveisfera-llvm_3.7-epel-7.repo
[daveisfera-llvm_3.7]
name=Copr repo for llvm_3.7 owned by daveisfera
baseurl=https://copr-be.cloud.fedoraproject.org/results/daveisfera/llvm_3.7/epel-7-$basearch/
type=rpm-md
skip_if_unavailable=True
gpgcheck=1
gpgkey=https://copr-be.cloud.fedoraproject.org/results/daveisfera/llvm_3.7/pubkey.gpg
repo_gpgcheck=0
enabled=1
enabled_metadata=1

Then just install llvm as normal:

sudo yum install clang llvm llvm-devel llvm-static
Note: Make sure to run all these steps as the moebooru user.

http://rayhightower.com/blog/2014/02/06/installing-rubinius-using-rvm/

The right way to use it is just plain jane on the server. Installation is easy using rvm. Run these as the current user:

Refresh the rvm repos:

rvm get head

Install rubinius. Compilation will take a while.

Next, choose whether you want to use rubinius or jruby. Both of them are high performance and able to make use of the Puma webserver.

rvm install rbx # only for rubinius
rvm install jruby # only for jruby

After installation, choose Rubinius (rbx) as the default ruby version to use.

rvm list

This will list out various ruby versions. Choose the `rbx` or `jruby` option.

rvm alias create default rbx # using rubinius
rvm alias create default jruby # using jruby

Finally, check your current ruby version to ensure that Rubinius is default.

ruby -v

Now that your ruby type is set up, install bundler.

gem install bundler

Setup Rubinius with Zonio Repo

Warning: Unfortunately Zonio does not keep their rubinius builds updated, so this method is depreciated.

Currently, just running normal ruby MRI should be sufficient for most tasks, but Rubinius can add significant performance boosts.

First, if you have normal ruby MRI currently installed, you should uninstall it.

Add the Zonio repository to the new file /etc/yum.repos.d/zonio.repo to add Rubinius:

[zonio]
name=Zonio $releasever - $basearch
baseurl=https://zonio.net/repos/epel/$releasever/$basearch
enabled=1
gpgcheck=1
gpgkey=https://zonio.net/repos/GPG-Key-Zonio
priority=5

Then, just install rubinius, and verify that:

sudo yum install rubinius rubinius-devel
ruby -v

Finally, log in as the moebooru user and install bundler:

sudo -u moebooru gem install bundler --user-install

Finally, edit moebooru's ~/.profile and add the correct PATH for your bundler (for example, /var/www/booru.eikonos.org/.gem/rbx/2.2/bin, may differ with your ruby version)

PATH=$PATH:/var/www/booru.eikonos.org/.gem/rbx/2.2/bin
Warning: Do not use the root user to install ruby gems! They will not be propogated to the right places.

Notice that you should not install normal ruby MRI afterwards. If you have a good reason to, follow these instructions:

https://zonio.net/rubinius_rpm_packages/

SELinux Permissions

If using SELinux (which we highly recommend), you will need the following policies

needed for proxy pass

sudo chcon -Rt httpd_sys_content_t /var/www/ # allow nginx to access folders
sudo setsebool httpd_can_network_connect 1 -P # allows reverse proxy
sudo setsebool -P httpd_can_network_memcache 1 # allows memcache

needed to serve from moebooru user's directory, but only shared/ and public folders

setsebool -P httpd_enable_homedirs 1
sudo semanage fcontext -a -t httpd_sys_content_t '/var/www/booru.eikonos.org/shared(/.*)?'
sudo restorecon -R -v /var/www/booru.eikonos.org/shared
sudo semanage fcontext -a -t httpd_sys_content_t '/var/www/booru.eikonos.org/public(/.*)?'
sudo restorecon -R -v /var/www/booru.eikonos.org/public

https://www.pckr.co.uk/selinux-nginx-and-reverse-proxying-2/

https://www.digitalocean.com/community/tutorials/an-introduction-to-selinux-on-centos-7-part-2-files-and-processes

The last step is to run the final allows.

sudo grep nginx /var/log/audit/audit.log | audit2allow -M nginx > nginx.te

View this and see that it is correct (such that no suspicious rule allows are inside). Then run:

sudo grep nginx /var/log/audit/audit.log | audit2allow -M nginx 
sudo semodule -i nginx.pp

http://axilleas.me/en/blog/2013/selinux-policy-for-nginx-and-gitlab-unix-socket-in-fedora-19/

Setup Moebooru

Now, conduct the setup and get dependencies.

sudo yum install gcc gcc-c++ ImageMagick jhead libxslt-devel git libyaml-devel openssl-devel pcre-devel readline-devel

First, do a bundle config for pg since we're using a specific postgresql version:

bundle config build.pg --with-pg-config=/usr/pgsql-9.5/bin/pg_config

Generate your secret key, which is used for salts and such.

bundle exec rake secret

Install the ruby packages for the moebooru user only (under the directory ./vendor/bundle):

bundle install --path vendor/bundle

Create config/database.yml and config/local_config.rb from the .example files, and configure them accordingly. Then set chmod 700 so only the moebooru user can read the database password.

chmod 600 /var/www/booru.eikonos.org/config/database.yml

Initialize database with bundle exec rake db:reset (there will be some errors reported which is expected)

Run bundle exec rake db:migrate

Now, you need to provide the correct permissions to the public folder:

chmod 755 /var/www/booru.eikonos.org/public

Start the server (bundle exec unicorn or bundle exec puma if using JRuby/Rubinius)

Finally, set moebooru to a non login user:

sudo chsh -s /bin/false moebooru

Customize Header Image and Branding

By default, Moebooru comes with the Yande.re header image and branding, as the site developed the moebooru engine. You should definitely consider removing the original branding unless your site is private.

app/assets/images

public/favicon.ico

Enable Memcached

Memcached is a high performance caching solution and is needed to have Moebooru enumerate posts and create the /posts pagination bar. Follow these instructions to install Memcached. You need at least 2GB free RAM to provide.

sudo yum install memcached

http://www.liquidweb.com/kb/how-to-install-memcached-on-centos-7/

Edit /etc/sysconfig/memcached and set CACHESIZE=2048 (2GB RAM) if possible.

Then set memcached to start at every boot:

sudo systemctl restart memcached

Config-based Activation

Add these options to the following file. When you start moebooru again, memcached will be active.

config/local_config.rb
# The server and port where the memcache client can be accessed. Only relevant if you enable caching.
CONFIG["memcache_servers"] = ["localhost:11211"]
# This enables various caching mechanisms. You must have memcache (and the memcache-client ruby gem) installed in order for caching to work.
CONFIG["enable_caching"] = true

ENV-based Memcached Activation

Note: We're not really sure whether the below method of enabling caching works. Instead, we use config-based activation as stated above.

Activate it by appending a bash variable to the puma command: MB_MEMCACHE_SERVERS="<url-to-memcached>" . Here are some examples.

TCP: MB_MEMCACHED_SERVERS='127.0.0.1:11211' RAILS_ENV=production bundle exec puma -e production

UNIX Socket: MB_MEMCACHED_SERVERS='127.0.0.1:11211' RAILS_ENV=production bundle exec puma -C shared/puma.rb

SELinux Permissions

You will probably need to allow it through SELinux:

https://major.io/2011/09/07/getting-apache-php-and-memcached-working-with-selinux/

Production Mode

By default, Moebooru runs in development mode, which can be slow. Here are the steps to set up Production mode.

Preparation

First, you need to create the database, and pregenerate the javascript/css (do this every time you update):

RAILS_ENV=production bundle exec rake db:reset
RAILS_ENV=production bundle exec rake assets:precompile

Then, you need to provide the correct permissions to the public folder:

chmod 755 /var/www/booru.eikonos.org/public

Serve static files with Nginx

Create an Nginx config under /etc/nginx/conf.d/. Make sure to change the server_name, and the root /var/www/booru.eikonos.org/public to root /YOUR/MOEBOORU/PATH/public.

Note: If you are using a different port for puma, (by adding -p 3000 to the serving command), also change the port accordingly below.

server {
    listen 80;
    server_name booru.eikonos.org;
    
    # directory of static assets, first generate with the command:
    # RAILS_ENV=production bundle exec rake assets:precompile
    root /var/www/booru.eikonos.org/public;

    try_files $uri/index.html $uri @app;

    location @app {
      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;
      # Fix the "It appears that your reverse proxy set up is broken" error.
      proxy_pass http://127.0.0.1:9292;
      proxy_read_timeout 90;
      proxy_redirect http://127.0.0.1:9292 http://$server_name;
    }
    
    error_page 500 502 503 504 /500.html;
    client_max_body_size 4G;
    keepalive_timeout 10;
}

Run the Server

Finally, to run the server (default is port 9292), run one of the following commands:

Ruby:

bundle exec unicorn -p 9292 -E production

Rubinius, JRuby:

RAILS_ENV=production bundle exec puma -e production

Serve with UNIX Socks in Production Mode

for even more effectiveness, use a UNIX sock: https://www.digitalocean.com/community/tutorials/how-to-deploy-a-rails-app-with-puma-and-nginx-on-ubuntu-14-04

Figure out the amount of CPU cores you have:

grep -c processor /proc/cpuinfo

Create the following folders in your application directory:

mkdir -p shared/pids shared/sockets shared/log

Place the following into <app_dir>/shared/puma.rb:

# Change to match your CPU core count
workers 8

# Min and Max threads per worker
threads 1, 6

app_dir = File.expand_path("../..", __FILE__)
shared_dir = "#{app_dir}/shared"

# Default to production
rails_env = ENV['RAILS_ENV'] || "production"
environment rails_env

# Set up socket location
bind "unix://#{shared_dir}/sockets/puma.sock"

# Logging
stdout_redirect "#{shared_dir}/log/puma.stdout.log", "#{shared_dir}/log/puma.stderr.log", true

# Set master PID and state locations
pidfile "#{shared_dir}/pids/puma.pid"
state_path "#{shared_dir}/pids/puma.state"
activate_control_app

on_worker_boot do
  require "active_record"
  ActiveRecord::Base.connection.disconnect! rescue ActiveRecord::ConnectionNotEstablished
  ActiveRecord::Base.establish_connection(YAML.load_file("#{app_dir}/config/database.yml")[rails_env])
end

Change the Nginx server config to the following:

upstream app {
    # Path to Puma SOCK file, as defined previously
    server unix:/var/www/booru.eikonos.org/shared/sockets/puma.sock fail_timeout=0;
}

server {
    listen 80;
    server_name booru.eikonos.org;
    
    # directory of static assets, first generate with the command:
    # RAILS_ENV=production bundle exec rake assets:precompile
    root /var/www/booru.eikonos.org/public;

    try_files $uri/index.html $uri @app;

    location @app {
      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;
      # Fix the "It appears that your reverse proxy set up is broken" error.
      proxy_pass http://app;
      proxy_read_timeout 90;
      proxy_redirect http://app http://$server_name;
    }
    
    error_page 500 502 503 504 /500.html;
    client_max_body_size 4G;
    keepalive_timeout 10;
}

Finally, to run the server, use the following:

bundle exec puma -C shared/puma.rb

Systemd Service

https://github.com/puma/puma/blob/master/docs/systemd.md

Save this to /usr/systemd/system/moebooru.service. There are two versions, one for TCP and one for unix socket. Change the WorkingDirectory accordingly.

TCP

[Unit]
Description=Moebooru's Puma HTTP Server
Requires=redis.service postgresql-9.5.service
Wants=postgresql-9.5.service memcached.service
After=network.target postgresql-9.5.service

# Uncomment for socket activation (see below)
# Requires=puma.socket

[Service]
# Foreground process (do not use --daemon in ExecStart or config.rb)
Type=simple

# Preferably configure a non-privileged user
User=moebooru

# Specify the path to your puma application root
WorkingDirectory=/var/www/booru.eikonos.org

# Helpful for debugging socket activation, etc.
# Environment=PUMA_DEBUG=1

# The command to start Puma
# Here we are using a binstub generated via:
# `bundle binstubs puma --path ./sbin`
# in the WorkingDirectory (replace <WD> below)
# You can alternatively use `bundle exec --keep-file-descriptors puma`
# ExecStart=<WD>/sbin/puma -b tcp://0.0.0.0:9292 -b ssl://0.0.0.0:9293?key=key.pem&cert=cert.pem

# Alternatively with a config file (in WorkingDirectory) and
# comparable `bind` directives
ExecStart=/bin/bash -c 'RAILS_ENV=production /usr/bin/bundle exec puma -e production'

Restart=always

[Install]
WantedBy=multi-user.target

UNIX Socket

Two Systemd services are needed: one for the socket and one for the application.

/usr/systemd/system/moebooru.service

[Unit]
Description=Puma HTTP Server
Requires=redis.service postgresql-9.5.service
Wants=postgresql-9.5.service memcached.service
After=network.target postgresql-9.5.service

# Uncomment for socket activation (see below)
# Requires=puma.socket

[Service]
# Foreground process (do not use --daemon in ExecStart or config.rb)
Type=simple

# Preferably configure a non-privileged user
# User=

# Specify the path to your puma application root
# WorkingDirectory=

# Helpful for debugging socket activation, etc.
# Environment=PUMA_DEBUG=1

# The command to start Puma
# Here we are using a binstub generated via:
# `bundle binstubs puma --path ./sbin`
# in the WorkingDirectory (replace <WD> below)
# You can alternatively use `bundle exec --keep-file-descriptors puma`
# ExecStart=<WD>/sbin/puma -b tcp://0.0.0.0:9292 -b ssl://0.0.0.0:9293?key=key.pem&cert=cert.pem

# Alternatively with a config file (in WorkingDirectory) and
# comparable `bind` directives
# ExecStart=<WD>/sbin/puma -C config.rb

Restart=always

[Install]
WantedBy=multi-user.target

Grab some code from here?

https://github.com/puma/puma/issues/976

SSL Certificates

While this is beyond the scope of this guide, you should strongly consider using SSL certificates, which are now free with Let's Encrypt.

https://www.digitalocean.com/community/tutorials/how-to-secure-nginx-with-let-s-encrypt-on-centos-7