Consegui persistir com sucesso minhas alterações de configuração do nginx ModSecurity entre as execuções de launcher rebuild app da seguinte forma:
Primeiro, atualizamos a cópia local de install-nginx que veio do repositório discourse_docker e está clonada em /var/discourse/.
cd /var/discourse/image/base
cp install-nginx install-nginx.`date "+%Y%m%d_%H%M%S"`.orig
# adicionar um bloco para fazer checkout do módulo nginx do modsecurity logo antes de baixar o código-fonte do nginx
grep 'ModSecurity' install-nginx || sed -i 's%\(curl.*nginx\.org/download.*\)%# mod_security\napt-get install -y libmodsecurity-dev modsecurity-crs\ncd /tmp\ngit clone --depth 1 https://github.com/SpiderLabs/ModSecurity-nginx.git\n\n\1%' install-nginx
# atualizar a linha de configuração para incluir o módulo ModSecurity feito checkout acima
sed -i '/ModSecurity/! s%^[^#]*./configure \(.*nginx.*\)%#./configure \1\n./configure \1 --add-module=/tmp/ModSecurity-nginx%' install-nginx
# adicionar uma linha na seção de limpeza
grep 'rm -fr /tmp/ModSecurity-nginx' install-nginx || sed -i 's%\(rm -fr.*/tmp/nginx.*\)%rm -fr /tmp/ModSecurity-nginx\n\1%' install-nginx
Note que o arquivo Dockerfile responsável por executar o script install-nginx é executado quando a imagem é construída. E a imagem é construída apenas pela equipe do Discourse antes de ser enviada para o Docker Hub. Quando o comando Discourse ./launcher rebuild app é executado, ele aciona (se houver atualizações disponíveis) um docker pull, que busca a última imagem Docker do Discourse no Docker Hub. Novamente, isso não reconstrói a imagem, executa o Dockerfile ou executa o script install-nginx modificado acima.
A única maneira (que eu conheço) de acionar a execução do script bash install-nginx atualizado (que é executado pelo Dockerfile) é fazer com que o Docker construa uma nova imagem. Por exemplo, isso aciona o Docker a construir uma nova imagem chamada discourse_modsecurity — que será construída usando o script install-nginx modificado localmente:
docker build --tag 'discourse_modsecurity' /var/discourse/image/base/
Infelizmente, não consegui encontrar uma maneira de dizer ao launcher para usar uma imagem personalizada (especificar um run-image usa a imagem especificada diretamente, sem executar os modelos contra ela — conforme necessário para realmente configurar [em vez de apenas instalar] o nginx). Então, substituímos a variável image definida no script launcher para usar nossa nova imagem Docker local chamada discourse_modsecurity:
# substituir a linha "image="discourse/base:<version>" por 'image="discourse_modsecurity"'
grep 'discourse_modsecurity' launcher || sed --in-place=.`date "+%Y%m%d_%H%M%S"` '/base_image/! s%^\(\s*\)image=\(.*\)$%#\1image=\2\n\1image="discourse_modsecurity"%' /var/discourse/launcher
Agora, adicionamos um novo arquivo de modelo para configurar nossas configurações do nginx para incluir os arquivos/blocos necessários do modsecurity:
cat << EOF > /var/discourse/templates/web.modsecurity.template.yml
run:
- exec:
cmd:
- sudo apt-get install -y modsecurity-crs
- cp /etc/modsecurity/modsecurity.conf-recommended /etc/modsecurity/modsecurity.conf
- sed -i 's/SecRuleEngine DetectionOnly/SecRuleEngine On/' /etc/modsecurity/modsecurity.conf
- sed -i 's^\(\s*\)[^#]*SecRequestBodyInMemoryLimit\(.*\)^\1#SecRequestBodyInMemoryLimit\2^' /etc/modsecurity/modsecurity.conf
- sed -i '/nginx/! s%^\(\s*\)[^#]*SecAuditLog \(.*\)%#\1SecAuditLog \2\n\1SecAuditLog /var/log/nginx/modsec_audit.log%' /etc/modsecurity/modsecurity.conf
- file:
path: /etc/nginx/conf.d/modsecurity.include
contents: |
################################################################################
# File: modsecurity.include
# Version: 0.1
# Purpose: Defines mod_security rules for the discourse vhost
# This should be included in the server{} blocks nginx vhosts.
# Author: Michael Altfield <michael@opensourceecology.org>
# Created: 2019-11-12
# Updated: 2019-11-12
################################################################################
Include "/etc/modsecurity/modsecurity.conf"
# OWASP Core Rule Set, instalado a partir do pacote 'modsecurity-crs' no debian
Include /etc/modsecurity/crs/crs-setup.conf
Include /usr/share/modsecurity-crs/rules/*.conf
- replace:
filename: "/etc/nginx/conf.d/discourse.conf"
from: /server.+{/
to: |
server {
modsecurity on;
modsecurity_rules_file /etc/nginx/conf.d/modsecurity.include;
EOF
E adicionamos este modelo (templates/web.modsecurity.template.yml) ao bloco de modelos do arquivo de configuração YAML do nosso app, para que fique algo assim:
[root@osestaging1 discourse]# vim /var/discourse/containers/app.yml
...
[root@osestaging1 discourse]# grep -A 6 'templates:' /var/discourse/containers/app.yml
templates:
- "templates/postgres.template.yml"
- "templates/redis.template.yml"
- "templates/web.template.yml"
- "templates/web.ratelimited.template.yml"
- "templates/web.socketed.template.yml"
- "templates/web.modsecurity.template.yml"
[root@osestaging1 discourse]#
Agora, ao reconstruir o app Docker do Discourse, ele usará sua nova imagem Docker discourse_modsecurity com nginx e modsecurity, e configurará o nginx para usar o OWASP CRS.
/var/discourse/launcher rebuild app