guh.me - gustavo's personal blog

Como configurar um servidor para hospedar suas aplicações em Ruby

Serviços como Heroku, AppFog, EngineYard e afins são uma mão na roda, mas muito caros para profissionais desfavorecidos, estudantes e pessoas sovinas. Uma alternativa viável é a locação de uma máquina virtual (VPS) e sua configuração “na raça”, mas isso não é lá muito fácil para marinheiros de primeira viagem… Neste artigo ensinarei a configurar um VPS (ou servidor dedicado) para hospedar aplicações em Ruby baseadas em Rack.

Antes de mais nada, você precisa contratar um serviço de hospedagem. Há muitas opções de provedores no mercado, e eu pessoalmente recomendo o DigitalOcean (referral, yay!). Eles possuem planos a partir de US$5 e ótimas configurações com SSD, o que dá um gás no desempenho. Para este artigo, utilizei a configuração mais básica, com 1 CPU, 512Mb de RAM, 20Gb de SSD e 1Tb de transferência de dados.

Carreguei na máquina a imagem do Ubuntu Server 13.04 32bits, pois, além de ser estável, têm repositórios atualizados e completos. Mas por que você não utilizou a distro <sua distro favorita aqui>? Ubuntu é muito <reclamação aleatória>. Bom, escolhi o Ubuntu porque ele é fácil de utilizar, e eu não sou sysadmin. Se você gosta de usar o Slackware com um kernel customizado, problema seu.

Bom, vamos começar. Conecte-se ao seu servidor como root e faça login.

1. Mude a senha de root

O primeiro passo é mudar a senha do usuário root. Escolha uma senha forte, com muita entropia e guarde-a em um lugar seguro. Uma hash MD5 dá para o gasto se você tiver preguiça de pensar numa senha boa. Você terá de digitar a senha e a confirmar.

passwd

2. Crie um novo usuário

Você não quer ficar logando em seu servidor como root, e muito menos permitir que alguém possa fazê-lo via SSH. Para isto, precisaremos criar um novo usuário. Para criar um novo usuário, use o comando adduser e digite os dados requisitados (senha, nome, etc).

adduser <username>

Em seguida, adicione o usuário criado ao arquivo sudoers, assim você poderá executar comandos com permissões especiais.

visudo

Adicione a seguinte linha ao arquivo, substituindo <username> pelo nome do usuário criado.

<username> ALL=(ALL) ALL

3. Limite o acesso via SSH

O próximo passo é limitar o acesso via SSH apenas para usuários autorizados, e bloquear para root.

Abra o arquivo de configuração de SSH:

nano /etc/ssh/sshd_config

Busque pelas seguintes linhas e mude seus respectivos valores para ficarem como abaixo, lembrando de substituir <username> pelo nome do usuário criado.

PermitRootLogin no
AllowUsers <username>

Reinicie o daemon de SSH. Sua conexão não será perdida.

service ssh restart

4. Atualize os pacotes e resolva as dependências

Para manter o servidor seguro, deve-se atualizar todos os pacotes instalados.

apt-get update
apt-get upgrade

Em seguida, precisamos instalar as dependências necessárias para que o servidor possa ser configurado. Isto inclui instalar SQLite3, vim, ferramentas para compilação (vamos compilar o Ruby e o nginx), OpenSSL e Git.

sudo apt-get install git-core curl build-essential vim libcurl4-openssl-dev bison openssl libreadline6 libreadline6-dev curl git-core zlib1g zlib1g-dev libssl-dev libyaml-dev libsqlite3-0 libsqlite3-dev sqlite3 libxml2-dev libxslt-dev autoconf

Por último, vamos instalar uma ferramenta para instalar automaticamente as atualizações de segurança.

Mas Gustavo, e se uma dessas atualizações quebrar o servidor?

Não é impossível, mas é melhor que o servidor quebre, fique fora do ar e seja reparado do que seja invadido e sofra danos irreparáveis.

A ferramenta unattended-upgrades pode ser instalada a partir dos repositórios padrão:

apt-get install unattended-upgrades

Após a instalação, devemos configurar a ferramenta:

$ vim /etc/apt/apt.conf.d/10periodic

Adicione as seguintes linhas ao arquivo e o salve.

APT::Periodic::Update-Package-Lists "1";
APT::Periodic::Download-Upgradeable-Packages "1";
APT::Periodic::AutocleanInterval "7";
APT::Periodic::Unattended-Upgrade "1";

Ainda precisamos assegurar que sejam instaladas somente as atualizações de segurança.

vim /etc/apt/apt.conf.d/50unattended-upgrades

Modifique o bloco Unattended-Upgrade::Allowed-Origins para que ele fique como abaixo, somente com as origens de segurança não comentadas:

Unattended-Upgrade::Allowed-Origins {
    "Ubuntu lucid-security";
  //"Ubuntu lucid-updates";
};

5. Instale o RVM e o Ruby

Vamos utilizar o RVM para compilar e instalar o Ruby e gerenciar nossas gems. Daí você pergunta, mas porque RVM e não rbenv? Porque eu quero, e porque não é preciso configurar o RVM.

Digite os comandos como root e aguarde a compilação e instalação da última versão do Ruby (2.0 em 19/06/2013).

\curl -L https://get.rvm.io | bash -s stable --ruby
source /etc/profile.d/rvm.sh

6. Instale o nginx e o Passenger

Chegou a vez de instalar o nginx, nosso servidor web (tá, proxy HTTP e reverso) e o Phusion Passenger, um servidor de aplicações baseadas em Rack (Sinatra, Rails, o universo e tudo mais ;). Há várias outras configurações disponíveis como Apache + Passenger, nginx + Puma e Unicorn + nginx. Vai de sua preferência e necessidades, mas a priori o Passenger é o mais fácil de instalar e configurar, e bom o suficiente para a maioria das aplicações.

O Passenger vem empacotado em uma gem, então é bem fácil o instalar. Quando a instalação começar, escolha a opção 1, pois ela baixará, compilará e instalará o nginx. Escolha o diretório padrão como /opt/nginx.

gem install passenger
rvmsudo passenger-install-nginx-module
chown -R <username>:<username> /home/<username>/.rvm

Em seguida precisamos criar um script de inicialização para o nginx, pois infelizmente a instalação pelo Passenger traz o brinquedo sem pilhas.

sudo vim /etc/init.d/nginx

Cole o seguinte script no buffer - ele não faz nada mais do que criar atalhos para gerenciar o nginx.

#! /bin/sh

### BEGIN INIT INFO
# Provides:          nginx
# Required-Start:    $all
# Required-Stop:     $all
# Default-Start:     2 3 4 5
# Default-Stop:      0 1 6
# Short-Description: starts the nginx web server
# Description:       starts nginx using start-stop-daemon
### END INIT INFO

PATH=/opt/nginx/sbin:/sbin:/bin:/usr/sbin:/usr/bin
DAEMON=/opt/nginx/sbin/nginx
NAME=nginx
DESC=nginx

test -x $DAEMON || exit 0

# Include nginx defaults if available
if [ -f /etc/default/nginx ] ; then
        . /etc/default/nginx
fi

set -e

case "$1" in
  start)
        echo -n "Starting $DESC: "
        start-stop-daemon --start --quiet --pidfile /opt/nginx/logs/$NAME.pid \
                --exec $DAEMON -- $DAEMON_OPTS
        echo "$NAME."
        ;;
  stop)
        echo -n "Stopping $DESC: "
        start-stop-daemon --stop --quiet --pidfile /opt/nginx/logs/$NAME.pid \
                --exec $DAEMON
        echo "$NAME."
        ;;
  restart|force-reload)
        echo -n "Restarting $DESC: "
        start-stop-daemon --stop --quiet --pidfile \
                /opt/nginx/logs/$NAME.pid --exec $DAEMON
        sleep 1
        start-stop-daemon --start --quiet --pidfile \
                /opt/nginx/logs/$NAME.pid --exec $DAEMON -- $DAEMON_OPTS
        echo "$NAME."
        ;;
  reload)
          echo -n "Reloading $DESC configuration: "
          start-stop-daemon --stop --signal HUP --quiet --pidfile     /opt/nginx/logs/$NAME.pid \
              --exec $DAEMON
          echo "$NAME."
          ;;
      *)
            N=/etc/init.d/$NAME
            echo "Usage: $N {start|stop|restart|reload|force-reload}" >&2
            exit 1
            ;;
    esac

    exit 0

Mude as permissões do arquivo e configure-o como um script de inicialização:

sudo chmod +x /etc/init.d/nginx
sudo /usr/sbin/update-rc.d -f nginx defaults
sudo service nginx start

Por último, mude o usuário do processo do nginx para o usuário que criamos:

sudo vim /opt/nginx/conf/mginx.conf

Busque pela linha user (se estiver comentada remova os comentários) e a modifique para:

user <username>;

7. Instale e configure o Fail2ban

O Fail2ban é um serviço que analisa logs e bane os IPs que parecem executar atividades maliciosas (busca por brechas, ataques de força bruta, etc). O serviço é bem flexível e é possível criar regras customizadas para vários serviços e/ou aplicações. Você pode encontrar mais detalhes sobre o que faremos neste link.

Para instalar o Fail2ban, execute o comando:

apt-get install fail2ban

Em seguida configure o Fail2ban para ler e atuar sobre os logs do nginx. O primeiro passo é criar um arquivo para guardar as suas configurações:

sudo cp /etc/fail2ban/jail.conf /etc/fail2ban/jail.local
sudo nano /etc/fail2ban/jail.local

Adicione as seguintes linhas no final do arquivo. Elas são nada mais do que regras que iremos definir no próximo passo.

[ssh]
enabled  = true
port     = ssh
filter   = sshd
logpath  = /var/log/auth.log
maxretry = 6

[nginx-auth]
enabled = true
filter  = nginx-auth
port    = http,https
logpath = /opt/nginx/logs/*error*.log
bantime = 600 # 10 minutes
maxretry = 6

[nginx-login]
enabled = true
filter  = nginx-login
port    = http,https
logpath = /opt/nginx/logs/*access*.log
bantime = 600 # 10 minutes
maxretry = 6

[nginx-badbots]
enabled = true
filter  = apache-badbots
port    = http,https
logpath = /opt/nginx/logs/*access*.log
bantime = 86400 # 1 day
maxretry = 1

[nginx-noscript]
enabled = true
port    = http,https
filter  = nginx-noscript
logpath = /opt/nginx/logs/*access*.log
axretry = 6
bantime = 86400 # 1 day

[nginx-proxy]
enabled = true
port    = http,https
filter  = nginx-proxy
logpath = /opt/nginx/logs/*access*.log
maxretry = 0
bantime  = 86400 # 1 day

Em seguida, crie os seguintes arquivos na pasta /etc/fail2ban/filter.d/ para definir as novas regras de filtragem:

nginx-proxy.conf

# Block IPs trying to use server as proxy.
#
# Matches e.g.
# 192.168.1.1 - - "GET http://www.something.com/
#
[Definition]
failregex = ^<HOST> -.*GET http.*
ignoreregex =

nginx-noscript.conf

# Block IPs trying to execute scripts such as .php, .pl, .exe and other funny scripts.
#
# Matches e.g.
# 192.168.1.1 - - "GET /something.php
#
[Definition]
failregex = ^<HOST> -.*GET.*(\.php|\.asp|\.exe|\.pl|\.cgi|\scgi)
ignoreregex =

nginx-auth.conf

# Blocks IPs that fail to authenticate using basic authentication
#
[Definition]

failregex = no user/password was provided for basic authentication.*client: <HOST>
           user .* was not found in.*client: <HOST>
           user .* password mismatch.*client: <HOST>
ignoreregex =

nginx-login.conf

# Blocks IPs that fail to authenticate using web application's log in page
#
# Scan access log for HTTP 200 + POST /sessions => failed log in
[Definition]
failregex = ^<HOST> -.*POST /sessions HTTP/1\.." 200
ignoreregex =

8. Configurando o firewall

O iptables é a escolha mais comum quando tratamos de firewall no Linux, e é por isso que utilizaremos o ufw. Mas como assim usar o ufw, se o iptables é a melhor escolha? Duh! O ufw (UncomplicatedFirewall) não é nada mais do que um frontend para o iptables, e é muito mais fácil de configurar.

Precisamos bloquear todas as portas exceto a 80 (HTTP), 443 (HTTPS) e 22 (SSH). Para isto digite os seguintes comandos, e voilà!

ufw allow 22
ufw allow 80
ufw allow 443
ufw enable

9. Monitoramento de logs

O Logwatch é um sistema de análise de logs que monitora certos eventos e cria um relatório nas áreas que você especificar. Em nosso caso, este relatório será enviado ao nosso email diariamente, relatando acessos ao sistema, pacotes modificados e mudanças no sistema de arquivos.

Primeiro, instale o Logwatch:

sudo apt-get install logwatch

Em seguida, crie um arquivo para agendar a tarefa:

vim /etc/cron.daily/00logwatch

Por fim, cole no arquivo o seguinte conteúdo, trocando <[email protected]> pelo seu endereço de email.

/usr/sbin/logwatch --output mail --mailto [email protected] --detail high

10. Instalando a aplicação

Como não vamos utilizar o Capistrano nem outra ferramenta para fazer o deploy, o faremos diretamente pelo Git. Para isto, faça login no servidor como o usuário que você criou no início do tutorial, e clone o repositório da aplicação em sua pasta raiz (~).

Instale as dependências de seu projeto (se não tiver uma Gemfile crie uma):

rvmsudo bundle install

Também será necessário criar um arquivo config.ru para informar ao Passenger como rodar a aplicação. Se você não tiver o arquivo, busque por um na documentação do framework utilizado.

O próximo passo será alterar as configurações do nginx para rodar a aplicação. Para tal, edite o arquivo de configurações do nginx:

sudo nano /opt/nginx/conf/nginx.conf

Adicione as seguintes linhas ao bloco http { … }, substituindo <username> pelo nome de seu usuário, <appdir> pelo diretório de sua aplicação e <domain> pelo nome de seu domínio.

http {
  server {
    listen 80;
    server_name <domain>;

    passenger_enable on;
    root /home/<username>/<appdir>/public;
  }
}

Reinicie o nginx.

sudo service nginx restart

Se tudo estiver certo você poderá visualizar sua aplicação na URL utilizada. Caso exista um erro na configuração o nginx e/ou o Passenger irão reclamar.

11. Configurando SSL

Se você for utilizar um certificado SSL em sua aplicação (o que é muito interessante), deverá tomar mais alguns passos para o instalar e configurar.

Primeiro, crie uma nova pasta em seu diretório raiz para abrigar os certificados. Pessoalmente, gosto de usar ~/ssl. Há abordagens melhores, mas esta é a mais prática.

mkdir ~/ssl
cd ~/ssl

Em seguida, é preciso gerar uma requisição de assinatura de certificado e uma chave. Substitua <domain> pelo nome de seu domínio. Ao ser indagado sobre seu Common name (CN), digite o nome do domínio que receberá o certificado, sem o www.

openssl req -new -newkey rsa:2048 -nodes -keyout <domain>.key -out <domain>.csr

Envie o arquivo CSR para sua entidade certificadora e aguarde o envio do certificado. Este processo varia de entidade para entidade.

Após receber o certificado (arquivo com extensão .crt), envie-o via SCP ou cole-o num novo arquivo na pasta ~/ssl:

nano ~/ssl/<domain>.crt

Por fim, vamos configurar o nginx para utilizar nossos certificados.

sudo nano /opt/nginx/conf/nginx.conf

Logo abaixo do bloco que você inseriu no passo anterior, insira o seguinte bloco, substituindo <domain> pelo seu domínio, <username> pelo nome do usuário criado e <appdir> pelo nome do diretório da aplicação.

server {
  listen 443;
  server_name <domain>;

  ssl on;
  ssl_certificate /home/<username>/<domain>.crt;
  ssl_certificate_key /home/<username>/<domain>.key;

  passenger_enable on;
  root /home/<username>/<appdir>/public;
}

Reinicie o nginx:

sudo service nginx restart

Se tude estiver correto o nginx e o Passenger iniciarão, e você poderá acessar sua aplicação via HTTP e HTTPS. Se ocorrer algum erro, se vire e procure por ele no Google. ;)

Num futuro próximo, ensinarei a fazer o deploy de uma aplicação em Rails 4 com Capistrano. É esperar para ver.