Tag: php-fpm

  • Reconfiguração de Apache e PHP-FPM para hospedagem de múltiplos sites

    Eu admito: na minha instalação do WordPress, eu fui preguiçoso. Eu não cheguei a configurar nenhum arquivo do Apache, eu só aproveitei o diretório padrão /var/www/html, a configuração padrão do PHP-FPM e foi isso.

    Mas, agora, é necessário preparar essa instalação para hospedar mais de uma aplicação em PHP, funcionando pelo mesmo proxy reverso, com alguns requisitos:

    1. Usar a mesma conexão do OpenVPN, com o mesmo IP, por simplicidade de manutenção.
    2. Manter os sites com algum isolamento um do outro, dificultando que outros sites sejam afetados caso um seja comprometido.

    Para começar, foi criada uma nova pasta para o blog em /var/www/pid1.com.br, para onde o conteúdo foi movido. Como o arquivo de configuração do WordPress fica acima da raiz do site, isso resulta na seguinte estrutura:

    • /var/www/pid1.com.br/wordpress – diretório com o WordPress
    • /var/www/pid1.com.br/wp-config.php – arquivo de configuração

    O requisito de utilizar a mesma conexão do OpenVPN com o mesmo IP é facilmente atendido com o mesmo tipo de recurso que permite o proxy reverso distinguir pra qual servidor uma requisição deve ser direcionada: a identificação pelo nome do host.

    Por padrão, as requisições HTTP vem com um parâmetro Host no cabeçalho. Então basta configurar os sites no Apache com o parâmetro ServerName definido e configurar o proxy reverso para encaminhar esse parâmetro.

    O arquivo de configuração no Apache fica:

    # /etc/httpd/conf.d/pid1.com.br.conf
    
    <VirtualHost *:80>
        ServerName pid1.com.br
    
        DocumentRoot /var/www/pid1.com.br/wordpress
    
        <Directory /var/www/pid1.com.br/wordpress>
            AllowOverride All
            Require all granted
        </Directory>
    
        ErrorLog /var/log/httpd/pid1.com.br_error.log
        CustomLog /var/log/httpd/pid1.com.br_access.log combined
    </VirtualHost>

    A configuração do Apache pode ser validada e o serviço reiniciado com os comandos:

    $ httpd -S
    # systemctl restart httpd 

    Na configuração do site no proxy reverso é necessário incluir a seguinte linha para o local /:

    proxy_set_header Host $host;

    Para cumprir o segundo requisito e criar um isolamento entre os sites, podem ser criados diferentes pools do PHP-FPM. Eles funcionam como processos independentes, que podem ser executados com configurações diferentes e por usuários diferentes.

    Para criar um usuário dedicado sem permissão de login:

    # useradd -s /usr/sbin/nologin pid1

    Para configurar o PHP-FPM, basta copiar a configuração padrão – nesse caso feito removendo as linhas comentadas:

    # grep -vE '^;|^\s*$' /etc/php-fpm.d/www.conf | tee /etc/php-fpm.d/pid1.com.br.conf

    E então, editar os parâmetros, substituindo o nome da configuração, nome de usuário e caminho do log.

    ; /etc/php-fpm.d/pid1.com.br.conf
    
    [pid1.com.br]
    user = pid1
    group = pid1
    
    listen = /run/php-fpm/pid1.sock
    listen.acl_users = apache,nginx
    listen.allowed_clients = 127.0.0.1
    
    pm = dynamic
    pm.max_children = 50
    pm.start_servers = 5
    pm.min_spare_servers = 5
    pm.max_spare_servers = 35
    
    slowlog = /var/log/php-fpm/pid1.com.br/pid1-slow.log
    
    php_admin_value[error_log] = /var/log/php-fpm/pid1.com.br/pid1-error.log
    php_admin_flag[log_errors] = on
    php_value[session.save_handler] = files
    php_value[session.save_path] = /var/lib/php/session
    php_value[soap.wsdl_cache_dir] = /var/lib/php/wsdlcache

    Os arquivos de log ficam por padrão em /var/log/php-fpm. No entanto, a propriedade da pasta é apache:root e as permissões são 700. Como o pool vai estar sendo executado por outro usuário, ele não teria permissão para escrever nessa pasta. Uma boa forma de solucionar isso é afrouxar as permissões pra pasta de logs e criar dentro dela uma pasta para cada pool com permissões mais restritivas, fazendo cada pool utilizar um diretório diferente.

    # chmod 755 /var/log/php-fpm
    # mkdir -m 700 /var/log/php-fpm/pid1.com.br
    # chown pid1:pid1 /var/log/php-fpm/pid1.com.br

    Então, basta configurar o virtual host do site para utilizar o pool desejado do PHP-FPM para arquivos de extensão .php, adicionando o seguinte bloco dentro do virtual host:

    <FilesMatch \.php$>
        SetHandler "proxy:unix:/run/php-fpm/pid1.sock|fcgi://localhost/"
    </FilesMatch>

    Por fim é necessário fazer os ajustes de permissão:

    • Atribuir a propriedade da pasta e arquivos do WordPress ou outra aplicação em PHP ao usuário e grupo do pool do PHP-FPM.
    • Ajustar a pasta de logs do PHP-FPM.
    • Ajustar o contexto do SELinux para permitir escrita pelo PHP-FPM – o padrão é httpd_sys_content_t, sem permissão de escrita.

    A propriedade da pasta pode ser ajustada com o seguinte comando:

    # chown -hR pid1:pid1 /var/www/pid1.com.br/wordpress

    Não esqueça de verificar se as permissões no sistema de arquivos estão adequadas: 755 ou 750 para diretórios, 644 ou 640 para arquivos, dependendo da necessidade. Assim, um processo do PHP–FPM de outro site não tem acesso de escrita na pasta.

    Eu prefiro utilizar 750 para o diretório do site, mantendo o padrão 755 e 644 para os demais diretórios e arquivos (excluindo exceções como o wp-config.php, que deve ficar com a permissão 600, por exemplo), adicionando o usuário administrador aos grupos dos usuários de serviço do PHP-FPM dos sites desejados para ter acesso de leitura sem necessidade do sudo.

    Com a permissão 750 na raiz, o usuário que executa o servidor web, apache, também precisa ser adicionado ao grupo dos usuários de serviço do PHP-FPM porque, apesar do processamento do PHP e a escrita no diretório serem feitos pelo usuário de serviço do PHP-FPM, o apache ainda precisa de acesso de leitura no diretório para ter acesso aos scripts em PHP.

    # usermod -aG pid1 daniel
    # usermod -aG pid1 apache

    Para adicionar o contexto de escrita do SELinux à pasta do site:

    # semanage fcontext -a -t httpd_sys_rw_content_t "/var/www/pid1.com.br/wordpress(/.*)?"
    # restorecon -Rv /var/www/pid1.com.br

    Alternativamente, você pode ter uma permissão mais restrita, deixando a escrita permitida apenas na pasta de posts e mídia do WordPress:

    # semanage fcontext -a -t httpd_sys_rw_content_t "/var/www/pid1.com.br/wp-content(/.*)?"

    E permitindo a escrita de forma temporária nos arquivos adequados ao performar atualizações do WordPress ou ajuste de configurações:

    # chcon -R -t httpd_sys_rw_content_t /var/www/pid1.com.br

    Podendo depois ser revertida aplicando o contexto configurado do SELinux:

    # restorecon -Rv /var/www/pid1.com.br

    Caso desejar uma configuração mais restrita ainda, pode-se deixar toda a pasta com propriedade do root, com exceção de wp-content/* e wp-config.php, e alterar para a propriedade do usuário que executa o PHP-FPM apenas quando for realizar uma atualização do WordPress.

    Para facilitar o ajuste de permissões na hora de atualizar, deixei dois scripts simples em /var/www/pid1.com.br:

    #!/bin/bash
    #/var/www/pid1.com.br/allow-write.sh
    
    script_path=$(realpath "$0")
    script_dir=$(dirname "$script_path")
    php_user=pid1
    
    chown -hR $php_user:$php_user $script_dir
    chcon -R -t httpd_sys_rw_content_t $script_dir
    setsebool httpd_can_network_connect on
    #!/bin/bash
    #/var/www/pid1.com.br/block-write.sh
    
    script_path=$(realpath "$0")
    script_dir=$(dirname "$script_path")
    php_user=pid1
    
    chown -hR root:root $script_dir
    chown -hR $php_user:$php_user $script_dir/wordpress/wp-content
    chown $php_user:$php_user $script_dir/wp-config.php
    restorecon -Rv $script_dir
    setsebool httpd_can_network_connect false

    Para configurar outros sites, basta seguir com a mesma estrutura:

    • Criar estrutura de pasta separada
    • Criar usuário para o PHP-FPM
    • Configurar pool independente do PHP-FPM com usuário dedicado
    • Ajustar permissões do sistema de arquivos e contexto do SELinux
    • Configurar o site no Apache
    • Configurar o site no proxy reverso
    • Reiniciar os serviços para que as configurações sejam aplicadas
    post scriptum