Servidor Web Apache trabalhando em modo chroot + Debian Wheezy

Aqui vou estar abordando a implementação de um servidor Web trabalhando em modo chroot dando suporte a php5, MySQL, Fail2ban, mod_security, mod-evasive para garantirmos a segurança de nosso servidor.

Prepare o seu sistema com o seguinte script http://wiki.douglasqsantos.com.br/doku.php/confinicialwheezy_en para que não falte nenhum pacote ou configuração.

Vamos primeiro fazer um update de nosso repositórios e a atualização de todo o sistema.

aptitude update && aptitude dist-upgrade -y

Agora vamos instalar o apache e módulo de chroot e o debootstrap para montar a nossa jaula.

aptitude install apache2  debootstrap libapache2-modsecurity -y

Montando a nossa jaula.

debootstrap wheezy /var/chroot http://ftp.br.debian.org/debian 

Copiando a configuração do nosso sistema para a jaula

cp -a /etc/resolv.conf /var/chroot/etc/
cp -a /etc/hosts /var/chroot/etc/
cp -a /etc/mime.types /var/chroot/etc/
cp -a /usr/share/zoneinfo/America/Sao_Paulo /var/chroot/usr/share/zoneinfo/America
cp -a /etc/adjtime /var/chroot/etc/

Instalando o apache dentro da jaula e o locales pois as vezes temos problemas com idioma dai já podemos corrigir este problema.

chroot /var/chroot apt-get update 
chroot /var/chroot aptitude dist-upgrade -y 
chroot /var/chroot apt-get install apache2.2-common apache2-utils locales -y

Acertando o idioma da jaula.

sed -i 's/# pt_BR.UTF-8 UTF-8/pt_BR.UTF-8 UTF-8/' /var/chroot/etc/locale.gen
chroot /var/chroot locale-gen

Não precisamos mais ajustar o pid do apache pois quando criamos a jaula o sistema cria um link do /var/chroot/var/run para o /run

Configurando o apache arquivo /etc/apache2/apache2.conf

echo "SecChrootDir /var/chroot" >> /etc/apache2/apache2.conf

Copiando os sites para a jaula caso já tenha algum.

cp -Ra /var/www/* /var/chroot/var/www/

Vamos testar o acesso a nossa jaula, vamos editar o index.html da nossa jaula e colocar um valor diferente do padrão para termos certeza que o apache esta nos mostrando os dados da jaula.

echo "<center><h1>Teste de chroot no Apache</h1></center> " > /var/chroot/var/www/index.html

Agora precisamos ajustar a jaula pois nesta nova versão do debootstrap ele cria alguns links que o modsecurity não aceita, com isso vamos remover os links e vamos criar os diretórios.

unlink /var/chroot/var/lock
unlink /var/chroot/var/run

Agora vamos criar os diretórios e ajustar os links.

mkdir -p /var/chroot/var/run/lock
ln -sf /var/chroot/var/run/lock /var/chroot/var/lock
mv /var/run/apache2.pid /var/chroot/var/run/apache2.pid
mv /var/run/apache2 /var/chroot/var/run/apache2
ln -s /var/chroot/var/run/apache2.pid /var/run/apache2.pid
ln -s /var/chroot/var/run/apache2 /var/run/apache2
rm -rf /var/chroot/var/run/apache2.pid

Agora vamos matar o processo do apache

killall -9 apache2

Vamos iniciar o apache

/etc/init.d/apache2 start

Vamos agora acessar o nosso site.

http://ip_servidor

Vai ter que aparecer a mensagem de teste que colocamos na jaula.

Podemos acompanhar os erros do apache como exemplo abaixo.

tail -f /var/log/apache2/error.log 
[Mon Dec 02 16:53:40 2013] [notice] ModSecurity: chroot checkpoint #1 (pid=4300 ppid=4295)
[Mon Dec 02 16:53:40 2013] [notice] ModSecurity for Apache/2.6.6 (http://www.modsecurity.org/) configured.
[Mon Dec 02 16:53:40 2013] [notice] ModSecurity: APR compiled version="1.4.6"; loaded version="1.4.6"
[Mon Dec 02 16:53:40 2013] [notice] ModSecurity: PCRE compiled version="8.30"; loaded version="8.30 2012-02-04"
[Mon Dec 02 16:53:40 2013] [notice] ModSecurity: LUA compiled version="Lua 5.1"
[Mon Dec 02 16:53:40 2013] [notice] ModSecurity: LIBXML compiled version="2.8.0"
[Mon Dec 02 16:53:41 2013] [notice] ModSecurity: chroot checkpoint #2 (pid=4301 ppid=1)
[Mon Dec 02 16:53:41 2013] [notice] ModSecurity: chroot successful, path=/var/chroot
[Mon Dec 02 16:53:41 2013] [notice] Apache/2.2.22 (Debian) configured -- resuming normal operations

A primeira parte já esta ok o apache esta trabalhando em modo chroot, agora temos que fazer o php5 e o mysql trabalhar com ele, e vamos também configurar mais alguns módulos para a segurança de nosso apache.

Instalando o php5 ele não precisa ficar na jaula.

aptitude install php5 libapache2-mod-php5 php5-gd php5-ps php5-cli php-pear php5-gd php5-mysql php5-imap php5-mcrypt php5-json -y
aptitude install php5-xmlrpc php5-dev php5-common fail2ban vsftpd postfix mysql-client -y
chroot /var/chroot apt-get install imagemagick php5-common -y

Acertando permissões

rm -rf /var/chroot/dev/*
for SEC in $(echo $(find /var/chroot/ -type f \( -perm -04000 -o -perm -02000 \) -print)); do chmod -s ${SEC}; done
chmod -x /var/chroot/bin/su
chmod -x /var/chroot/bin/stty

Configurando o módulo avasive para nos ajudar a prevenir os ataques do tipo DOS.

apt-get install libapache2-mod-evasive
echo "LoadModule evasive20_module /usr/lib/apache2/modules/mod_evasive20.so" > /etc/apache2/mods-available/evasive.load

Configurando o módulo /etc/apache2/mods-available/evasive.conf

vim /etc/apache2/mods-available/evasive.conf
<IfModule mod_evasive.c>
DOSHashTableSize 3097 
DOSPageCount 2            
DOSSiteCount 50            
DOSPageInterval 1          
DOSSiteInterval 1            
DOSBlockingPeriod 900  
DOSWhitelist 66.249.65.*
DOSWhitelist 66.249.66.*
# As ultimas são as que o google usa para indexar.
</IfModule>

Acertando o idioma do sistema

sed -i 's/# pt_BR.UTF-8 UTF-8/pt_BR.UTF-8 UTF-8/' /etc/locale.gen
locale-gen

Vamos montar um virtualhost para testarmos o nosso chroot /etc/apache2/sites-available/wiki.douglasqsantos.com.br

vim /etc/apache2/sites-available/wiki.douglasqsantos.com.br
<VirtualHost *:80>
 ServerName wiki.douglasqsantos.com.br
 ServerAlias douglasqsantos.com.br

 DocumentRoot "/var/www/website/frontend/"
 <Directory "/var/www/website/frontend/">
   Options -Indexes +FollowSymLinks +MultiViews
   AllowOverride All
   Order allow,deny
   allow from all
 </Directory>

 ScriptAlias /cgi-bin/ "/var/www/website/frontend/cgi-bin/"
 <Directory "/var/www/website/frontend/cgi-bin/">
   AllowOverride All
   Options ExecCGI -MultiViews +SymLinksIfOwnerMatch
   Order allow,deny
   Allow from all
 </Directory>

 ErrorLog ${APACHE_LOG_DIR}/wiki.douglasqsantos.com.br-error.log
 CustomLog ${APACHE_LOG_DIR}/wiki.douglasqsantos.com.br-access.log common
 
 ServerSignature Off
 
 IndexIgnore .??* *~ *# README RCS CVS *,v *,t *

 # Possible values include: debug, info, notice, info, error, crit,
 # alert, emerg.
 LogLevel info
</VirtualHost>

Vamos montar a base do nosso site

mkdir -p /var/chroot/var/www/website/frontend/cgi-bin
ln -sf /var/chroot/var/www/website /var/www/website

Criando o arquivo index de teste.

echo "<?php phpinfo(); ?>" > /var/www/website/frontend/index.php

Incluindo o shell /bin/true para o usuário ftp

echo "/bin/true" >> /etc/shells

Criando o usuário ftp para o nosso site

useradd -m -d /var/www/website -s /bin/true usuarioftp

Agora vamos definir uma senha para ele

passwd usuarioftp

Carregando o nosso site e descarregando os sites defaults

a2ensite wiki.douglasqsantos.com.br
a2dissite default 
a2dissite default-ssl 
a2enmod rewrite

Agora vamos configurar o mod_security insira no final do arquivo /etc/apache2/apache2.conf o conteúdo abaixo.

vim /etc/apache2/apache2.conf
[...]
#Final do arquivo
<IfModule mod_security.c>
 # Turn the filtering engine On or Off
 SecFilterEngine On

 # Make sure that URL encoding is valid
 SecFilterCheckURLEncoding On

 # Unicode encoding check
 SecFilterCheckUnicodeEncoding Off

 # Only allow bytes from this range
 SecFilterForceByteRange 0 255

 # Only log suspicious requests
 SecAuditEngine RelevantOnly

 # The name of the audit log file
 SecAuditLog /var/log/apache2/audit_log

 # Debug level set to a minimum
 SecFilterDebugLog /var/log/apache2/modsec_debug_log
 SecFilterDebugLevel 0

 # Should mod_security inspect POST payloads
 SecFilterScanPOST On

 # By default log and deny suspicious requests
 # with HTTP status 500
 SecFilterDefaultAction "deny,log,status:500"
</IfModule>

Vamos reiniciar agora o nosso apache.

/etc/init.d/apache2 restart

Agora vamos analisar os logs do apache

tail -f /var/log/apache2/error.log 
[Mon Dec 02 17:05:42 2013] [notice] ModSecurity: chroot checkpoint #1 (pid=13239 ppid=13234)
[Mon Dec 02 17:05:42 2013] [notice] ModSecurity for Apache/2.6.6 (http://www.modsecurity.org/) configured.
[Mon Dec 02 17:05:42 2013] [notice] ModSecurity: APR compiled version="1.4.6"; loaded version="1.4.6"
[Mon Dec 02 17:05:42 2013] [notice] ModSecurity: PCRE compiled version="8.30"; loaded version="8.30 2012-02-04"
[Mon Dec 02 17:05:42 2013] [notice] ModSecurity: LUA compiled version="Lua 5.1"
[Mon Dec 02 17:05:42 2013] [notice] ModSecurity: LIBXML compiled version="2.8.0"
[Mon Dec 02 17:05:43 2013] [notice] ModSecurity: chroot checkpoint #2 (pid=13240 ppid=1)
[Mon Dec 02 17:05:43 2013] [notice] ModSecurity: chroot successful, path=/var/chroot
[Mon Dec 02 17:05:43 2013] [notice] Apache/2.2.22 (Debian) PHP/5.4.4-14+deb7u5 configured -- resuming normal operations

Vamos também tirar a assinatura do nosso php

vim /etc/php5/apache2/php.ini
[...]
expose_php = Off
[...]

Agora já podemos reiniciar o nosso apache novamente para ver se vamos ter mais algum aviso ou erro.

/etc/init.d/apache2 restart

Vamos configurar o nosso MySQL para o apache ter acesso.

aptitude install mysql-server mysql-client php5-mysql -y

Agora vamos ajustar o MySQL

/etc/init.d/mysql stop
mv /var/run/mysqld /var/chroot/var/run/mysqld
ln -s /var/chroot/var/run/mysqld /var/run/mysqld

Agora podemos já inicializar o nosso MySQL

/etc/init.d/mysql start

Agora vamos ajustar as permissões do nosso site.

chown -R usuarioftp:www-data /var/chroot/var/www/website

Agora vamos configurar o logrotate para ir fazendo rotação dos logs do nosso site arquivo /etc/logrotate.d/websites

vim /etc/logrotate.d/websites
#/etc/logrotate.d/websites
/var/www/website/logs/*.log {
 weekly
 missingok
 rotate 6
 compress
 delaycompress
 notifempty
 create 640 www-data www-data
 sharedscripts
 postrotate
 /etc/init.d/apache2 reload > /dev/null
 endscript
}

Vamos configurar o vsftdp para podermos subir os arquivos para o site, os usuários somente vão poder ver o seu home eles vão ficar presos dentro da jaula arquivo /etc/vsftpd.conf

vim /etc/vsftpd.conf 
#/etc/vsftpd.conf
listen=YES
listen_ipv6=NO
anonymous_enable=NO
local_enable=YES
write_enable=YES
local_umask=002
anon_upload_enable=NO
anon_mkdir_write_enable=NO
dirmessage_enable=YES
use_localtime=YES
xferlog_enable=YES
connect_from_port_20=NO
xferlog_file=/var/log/vsftpd.log
xferlog_std_format=YES
idle_session_timeout=600
data_connection_timeout=120
nopriv_user=ftp
ftpd_banner=Bem-Vindo ao FTP douglasqsantos.com.br.
deny_email_enable=NO
chroot_local_user=YES
allow_writeable_chroot=YES
chroot_list_enable=NO
banned_email_file=/etc/vsftpd.banned_emails
chroot_list_file=/etc/vsftpd.chroot_list
ls_recurse_enable=YES
secure_chroot_dir=/var/run/vsftpd/empty
pam_service_name=vsftpd
rsa_cert_file=/etc/ssl/private/vsftpd.pem

Criando mais alguns arquivos necessários

Nesse arquivos podemos colocar os usuários que vão poder sair da jaula.

touch /etc/vsftpd.chroot_list 

Nesse arquivo podemos colocar os emails que vão ser negados pelo vsftpd.

touch /etc/vsftpd.banned_emails

A Versão padrão do debian está com pau gera o seguinte erro quando loga no ftp podemos ver o bug em http://bugs.debian.org/cgi-bin/bugreport.cgi?bug=656900

ftp localhost
ftp: connect to address ::1: Connection refused
Trying 127.0.0.1...
Connected to localhost.
220 Bem-Vindo ao FTP douglasqsantos.com.br.
Name (localhost:root): usuarioftp
331 Please specify the password.
Password:
500 OOPS: vsftpd: refusing to run with writable root inside chroot()
Login failed.
ftp> quit
421 Service not available, remote server has closed connection

Agora vamos ajustar o vsftpd

echo "deb http://ftp.cyconet.org/debian wheezy-updates main non-free contrib" >> /etc/apt/sources.list.d/wheezy-updates.cyconet.list 
aptitude update; aptitude install -t wheezy-updates debian-cyconet-archive-keyring vsftpd

Agora que já instalamos o nosso pacote podemos remover este repositório

rm -rf /etc/apt/sources.list.d/wheezy-updates.cyconet.list 

Vamos reiniciar o nosso serviço.

/etc/init.d/vsftpd restart

Agora vamos testar o acesso via ftp.

ftp localhost
ftp: connect to address ::1: Connection refused
Trying 127.0.0.1...
Connected to localhost.
220 Bem-Vindo ao FTP douglasqsantos.com.br.
Name (localhost:root): usuarioftp
331 Please specify the password.
Password:
230 Login successful.
Remote system type is UNIX.
Using binary mode to transfer files.
ftp> ls
200 PORT command successful. Consider using PASV.
150 Here comes the directory listing.
drwxr-xr-x    3 1001     33           4096 Dec 02 17:26 frontend
226 Directory send OK.
ftp> quit
221 Goodbye.

Como pode ser notado conseguimos acessar o nosso servidor normalmente com o usuário que criamos.

Agora vamos configurar o Fail2ban para cuidar de nosso servidor arquivo /etc/fail2ban/jail.conf

vim /etc/fail2ban/jail.conf
#/etc/fail2ban/jail.conf
[DEFAULT]
ignoreip = 127.0.0.1, 10.0.0.0/23
bantime  = 600
maxretry = 3
backend = polling
destemail = email_do_webmaster
banaction = iptables-multiport
mta = sendmail
protocol = tcp
action_ = %(banaction)s[name=%(__name__)s, port=\"%(port)s\", protocol=\"%(protocol)s]
action_mw = %(banaction)s[name=%(__name__)s, port=\"%(port)s\", protocol=\"%(protocol)s]
 %(mta)s-whois[name=%(__name__)s, dest=\"%(destemail)s\", protocol=\"%(protocol)s]
action_mwl = %(banaction)s[name=%(__name__)s, port=\"%(port)s\", protocol=\"%(protocol)s]
 %(mta)s-whois-lines[name=%(__name__)s, dest=\"%(destemail)s\", logpath=%(logpath)s]
 
action = %(action_mwl)s

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

[pam-generic]
enabled = true
filter  = pam-generic
port = all
banaction = iptables-allports
port     = anyport
logpath  = /var/log/auth.log
maxretry = 3

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

[apache]
enabled = true
port    = http,https
filter  = apache-auth
logpath = /var/log/apache*/*error.log
maxretry = 3

[dominio.com.br]
enabled = true
port    = http,https
filter  = apache-auth
logpath = /var/www/website/logs/*error.log
maxretry = 3

[vsftpd]
enabled  = true
port     = ftp,ftp-data,ftps,ftps-data
filter   = vsftpd
logpath  = /var/log/vsftpd.log
maxretry = 3

[postfix]
enabled  = true
port     = smtp,ssmtp
filter   = postfix
logpath  = /var/log/mail.log

[sasl]
enabled  = true
port     = smtp,ssmtp,imap2,imap3,imaps,pop3,pop3s
filter   = sasl
logpath  = /var/log/mail.log

Agora vamos reiniciar o serviço para que o nosso servidor estejá com um agente análisando os logs e bloqueando quando necessário.

/etc/init.d/fail2ban restart

Agora vamos fazer mais alguns ajustes em nosso apache deixe como abaixo arquivo /etc/apache2/conf.d/security

vim /etc/apache2/conf.d/security
[...]
ServerTokens Prod
[...]
ServerSignature Off

E na jaula também /var/chroot/etc/apache2/conf.d/security

vim /var/chroot/etc/apache2/conf.d/security
[...]
ServerTokens Prod
[...]
ServerSignature Off

Agora é só reiniciar o apache.

/etc/init.d/apache2 restart

Agora é só acessar o site em http://wiki.douglasqsantos.com.br

Referências