Como usar o NGINX e o NGINX Plus com o SELinux

NGINX | 17 de agosto de 2018

Nota do editor – A publicação do blog intitulada “NGINX: alterações no SELinux ao atualizar para o RHEL 6.6 / CentOS 6.6” redireciona para esta página. Este artigo apresenta informações atualizadas e mais abrangentes.

As configurações padrão do Security-Enhanced Linux (SELinux) nas versões modernas do Red Hat Enterprise Linux (RHEL) e em distribuições relacionadas podem ser muito rigorosas, priorizando a segurança em vez da conveniência. Embora as configurações padrão não limitem o funcionamento do NGINX Open Source e do NGINX Plus em suas configurações padrão, outros recursos que você configurar poderão ser bloqueados, a menos que sejam explicitamente permitidos no SELinux. Este artigo descreve os possíveis problemas e as formas recomendadas de resolvê-los.

[Editor – Este artigo se aplica tanto ao NGINX Open Source quanto ao NGINX Plus. Para facilitar a leitura, o termo “NGINX” é usado em todo o texto.

O CentOS é uma distribuição relacionada, originalmente derivada do RHEL, e é compatível com o NGINX e o NGINX Plus. Além disso, o NGINX Plus é compatível com as distribuições relacionadas Amazon Linux e Oracle Linux. As configurações padrão do SELinux nessas distribuições podem ser diferentes das configurações do CentOS e do RHEL; consulte a documentação do fornecedor.]

Visão geral do SELinux

O SELinux é habilitado por padrão em servidores RHEL e CentOS modernos. Cada objeto do sistema operacional (processo, descritor de arquivo, arquivo etc.) é rotulado com um contexto do SELinux que define as permissões e operações que o objeto pode executar. No RHEL 6.6/CentOS 6.6 e versões posteriores, o NGINX é rotulado com o contexto httpd_t:

# ps auZ | grep nginxunconfined_u:system_r:httpd_t:s0 3234 ? Ss 0:00 nginx: master process /usr/sbin/nginx \
                                                -c /etc/nginx/nginx.conf
unconfined_u:system_r:httpd_t:s0 3236 ? Ss 0:00 nginx: worker process

O contexto httpd_t permite que o NGINX escute nas portas comuns de servidores web, acesse os arquivos de configuração em /etc/nginx e acesse o conteúdo no diretório raiz padrão (/usr/share/nginx). Ele não permite muitas outras operações, como encaminhar solicitações por proxy para servidores upstream ou se comunicar com outros processos por meio de sockets.

Desabilitando temporariamente o SELinux para o NGINX

Para desabilitar temporariamente as restrições do SELinux para o contexto httpd_t, permitindo que o NGINX execute as mesmas operações que em sistemas operacionais sem SELinux, atribua o contexto httpd_t ao domínio permissive. Consulte a próxima seção para obter detalhes.

# semanage permissive -a httpd_t

Alterando os modos do SELinux

O SELinux pode ser executado nos modos enforcing, permissive ou disabled (também chamados de domínios). Antes de fazer uma alteração na configuração do NGINX que possa violar as permissões padrão (estritas), você pode mudar o SELinux do modo enforcing para o modo permissive, no seu ambiente de teste (se disponível) ou de produção. No modo permissive, o SELinux permite todas as operações, mas registra aquelas que violariam a política de segurança no modo enforcing.

Para adicionar httpd_t à lista de domínios permissivos, execute este comando:

# semanage permissive -a httpd_t

Para remover httpd_t da lista de domínios permissivos, execute:

# semanage permissive -d httpd_t

Para definir globalmente o modo como permissive, execute:

# setenforce 0

Para definir globalmente o modo como enforcing, execute:

# setenforce 1

Resolvendo exceções de segurança do SELinux

No modo permissivo, as exceções de segurança são registradas no log de auditoria padrão do Linux, /var/log/audit/audit.log. Se você encontrar um problema que ocorre somente quando o NGINX está sendo executado no modo impositivo, analise as exceções registradas no modo permissivo e atualize a política de segurança para permiti-las.

Problema 1: a conexão com o proxy é proibida

Por padrão, a configuração do SELinux não permite que o NGINX se conecte a servidores HTTP, FastCGI ou outros servidores remotos, conforme indicado por uma mensagem de log de auditoria como a seguinte:

type=AVC msg=audit(1415714880.156:29): avc:  denied  { name_connect } for  pid=1349 \
  comm="nginx" dest=8080 scontext=unconfined_u:system_r:httpd_t:s0 \
  tcontext=system_u:object_r:http_cache_port_t:s0 tclass=tcp_socket
type=SYSCALL msg=audit(1415714880.156:29): arch=c000003e syscall=42 success=no \
  exit=-115 a0=b \a1=16125f8 a2=10 a3=7fffc2bab440 items=0 ppid=1347 pid=1349 \
  auid=1000 uid=497 gid=496 euid=497 suid=497 fsuid=497 egid=496 sgid=496 fsgid=496 \
  tty=(none) ses=1 comm="nginx" exe="/usr/sbin/nginx" \
  subj=unconfined_u:system_r:httpd_t:s0 key=(null)

O comando audit2why interpreta o código da mensagem (1415714880.156:29):

# grep 1415714880.156:29 /var/log/audit/audit.log | audit2whytype=AVC msg=audit(1415714880.156:29): avc:  denied  { name_connect } for  pid=1349 \
  comm="nginx" dest=8080 scontext=unconfined_u:system_r:httpd_t:s0 \
  tcontext=system_u:object_r:http_cache_port_t:s0 tclass=tcp_socket
 
        Was caused by:
        One of the following booleans was set incorrectly.
        Description:
        Allow httpd to act as a relay
 
        Allow access by executing:
        # setsebool -P httpd_can_network_relay 1
        Description:
        Allow HTTPD scripts and modules to connect to the network using TCP.
 
        Allow access by executing:
        # setsebool -P httpd_can_network_connect 1

A saída de audit2why indica que você pode permitir que o NGINX faça conexões de proxy habilitando uma ou ambas as opções booleanas httpd_can_network_relay e httpd_can_network_connect. Você pode habilitá-las temporária ou permanentemente; neste último caso, adicione a opção ‑P, conforme mostrado na saída.

Entendendo as opções booleanas

O comando sesearch fornece mais informações sobre as opções booleanas e fica disponível após a instalação do pacote setools (yum install setools). Aqui, mostramos a saída das opções httpd_can_network_relay e httpd_can_network_connect.

A opção booleana httpd_can_network_relay Boolean Option

Veja a saída do comando sesearch referente à opção httpd_can_network_relay:

# sesearch -A -s httpd_t -b httpd_can_network_relayFound 10 semantic av rules:
   allow httpd_t gopher_port_t : tcp_socket name_connect ;
   allow httpd_t http_cache_client_packet_t : packet { send recv } ;
   allow httpd_t ftp_port_t : tcp_socket name_connect ;
   allow httpd_t ftp_client_packet_t : packet { send recv } ;
   allow httpd_t http_client_packet_t : packet { send recv } ;
   allow httpd_t squid_port_t : tcp_socket name_connect ;
   allow httpd_t http_cache_port_t : tcp_socket name_connect ;
   allow httpd_t http_port_t : tcp_socket name_connect ;
   allow httpd_t gopher_client_packet_t : packet { send recv } ;
   allow httpd_t memcache_port_t : tcp_socket name_connect ;

Essa saída indica que httpd_can_network_relay permite que processos rotulados com o contexto httpd_t (como o NGINX) se conectem a portas de vários tipos, incluindo o tipo http_port_t:

# semanage port -l | grep http_port_thttp_port_t                    tcp      80, 81, 443, 488, 8008, 8009, 8443, 9000

Para adicionar mais portas (neste caso, 8082) ao conjunto de portas permitidas para http_port_t, execute:

# semanage port -a -t http_port_t -p tcp 8082

Se a saída desse comando informar que uma porta já está definida, como no exemplo a seguir, isso significa que a porta está incluída em outro conjunto. Não a reatribua a http_port_t, pois isso pode afetar negativamente outros serviços.

# semanage port -a -t http_port_t -p tcp 8080
/usr/sbin/semanage: Port tcp/8080 already defined
# semanage port -l | grep 8080
http_cache_port_t              tcp      3128, 8080, 8118, 8123, 10001-10010

A opção booleana httpd_can_network_connect Boolean Option

Veja a saída do comando sesearch referente à opção httpd_can_network_connect:

# sesearch -A -s httpd_t -b httpd_can_network_connectFound 1 semantic av rules:
   allow httpd_t port_type : tcp_socket name_connect ;

Essa saída indica que httpd_can_network_connect permite que processos rotulados com o contexto httpd_t (como o NGINX) se conectem a todos os tipos de soquete TCP que tenham o atributo port_type. Para listá-los, execute:

# seinfo -aport_type -x

Problema 2: o acesso ao arquivo é proibido

Por padrão, a configuração do SELinux não permite que o NGINX acesse arquivos fora de locais autorizados conhecidos, conforme indicado por uma mensagem de log de auditoria como a seguinte:

type=AVC msg=audit(1415715270.766:31): avc:  denied  { getattr } for  pid=1380 \
  comm="nginx" path="/www/t.txt" dev=vda1 ino=1084 \
  scontext=unconfined_u:system_r:httpd_t:s0 \
  tcontext=unconfined_u:object_r:default_t:s0 tclass=file

O comando audit2why interpreta o código da mensagem (1415715270.766:31):

# grep 1415715270.766:31 /var/log/audit/audit.log | audit2whytype=AVC msg=audit(1415715270.766:31): avc:  denied  { getattr } for  pid=1380 \
  comm="nginx" path="/www/t.txt" dev=vda1 ino=1084 \
  scontext=unconfined_u:system_r:httpd_t:s0 \
  tcontext=unconfined_u:object_r:default_t:s0 tclass=file
 
    Was caused by:
        Missing type enforcement (TE) allow rule.
 
        You can use audit2allow to generate a loadable module to allow this access.

Quando o acesso ao arquivo é proibido, você tem duas opções.

Opção 1: Modificar o rótulo do arquivo

Modifique o rótulo do arquivo para que o NGINX (como um processo rotulado com o contexto httpd_t ) possa acessar o arquivo:

# chcon -v --type=httpd_sys_content_t /www/t.txt

Por padrão, essa modificação é excluída quando o sistema de arquivos é rotulado novamente. Para tornar a alteração permanente, execute:

# semanage fcontext -a -t httpd_sys_content_t /www/t.txt
# restorecon -v /www/t.txt

Para modificar os rótulos de grupos de arquivos, execute:

# semanage fcontext -a -t httpd_sys_content_t '/www(/.*)?'# restorecon -Rv /www

Opção 2: Estenda as permissões do domínio httpd_t Domain Permissions

Estenda a política para httpd_t a fim de permitir o acesso a outros locais de arquivos:

# grep nginx /var/log/audit/audit.log | audit2allow -m nginx > nginx.te# cat nginx.te
 
module nginx 1.0;
 
require {
        type httpd_t;
        type default_t;
        type http_cache_port_t;
        class tcp_socket name_connect;
        class file { read getattr open };
}
 
#============= httpd_t ==============
allow httpd_t default_t:file { read getattr open };
 
#!!!! This avc can be allowed using one of these booleans:
#     httpd_can_network_relay, httpd_can_network_connect
allow httpd_t http_cache_port_t:tcp_socket name_connect;

Para gerar uma política compilada, inclua a opção -M:

# grep nginx /var/log/audit/audit.log | audit2allow -M nginx

Para carregar a política, execute semodule -i, depois verifique se a operação foi bem-sucedida com semodule -l:

# semodule -i nginx.pp
# semodule -l | grep nginx
nginx 1.0

Essa alteração persiste após reinicializações.

Problema 3: o NGINX não consegue se vincular a portas adicionais

Por padrão, a configuração do SELinux não permite que o NGINX escute (bind()) em portas TCP ou UDP diferentes das portas padrão permitidas no tipo http_port_t:

# semanage port -l | grep http_port_thttp_port_t                    tcp      80, 443, 488, 8008, 8009, 8443

Se você tentar configurar o NGINX para escutar em uma porta que não está na lista de permissões (com a diretiva listen no contexto http, stream ou mail da configuração do NGINX), ocorrerá um erro ao verificar (nginx -t) ou recarregar a configuração do NGINX, conforme indicado por esta entrada de log do NGINX:

YYYY/MM/DD hh:mm:ss [emerg] 46123#0: bind() to 0.0.0.0:8001 failed (13: Permission denied)

Você pode usar o comando semanage para adicionar a porta desejada (neste caso, 8001) ao tipo http_port_t:

# semanage port -a -t http_port_t -p tcp 8001

Recarregue o NGINX com a nova configuração.

# nginx -s reload

Problema 4: Há muitos arquivos abertos — Erro

Quando o limite do número de arquivos abertos (RLIMIT_NOFILE) é excedido, a seguinte mensagem aparece no log de erros:

Too many files are open

Quando o processo de trabalho do NGINX gera o erro

Na maioria dos casos, o processo de trabalho do NGINX relata esse erro, mas não é possível usar a diretiva worker_rlimit_nofile do NGINX para aumentar o limite, pois o SELinux não permite a chamada de sistema setrlimit() conforme relatado nas mensagens a seguir dos logs de erros e de auditoria.

  • Em /var/log/nginx/error.log no CentOS/RHEL 7.4+:
YYYY/MM/DD hh:mm:ss [alert] 12066#0: setrlimit(RLIMIT_NOFILE, 2342) failed (13: Permission denied)
  • Em /var/log/nginx/error.log para CentOS/RHEL 8.0+:
YYYY/MM/DD hh:mm:ss [alert] 3327#0: setrlimit(RLIMIT_NOFILE, 65535) failed (1: Operation not permitted)
  • Em /var/log/audit/audit.log para CentOS/RHEL 7.4+ e /var/log/messages para CentOS/RHEL 8.0+:
type=AVC msg=audit(1437731200.211:366): avc:  denied  { setrlimit } for pid=12066 \  comm="nginx" scontext=system_u:system_r:httpd_t:s0 \
  tcontext=system_u:system_r:httpd_t:s0 tclass=process

Para aumentar o limite, execute este comando como o usuário root:

$ setsebool -P httpd_setrlimit 1

Quando o processo mestre do NGINX gera o erro

Se o processo mestre do NGINX relatar o erro, você precisará atualizar o arquivo systemd unit do NGINX. Isso define o limite de descritores de arquivo para os processos mestre e de trabalho.

  1. Crie um diretório para a configuração do nginx.service:
$ mkdir /etc/systemd/system/nginx.service.d
  1. Adicione as seguintes linhas a /etc/systemd/system/nginx.service.d/nofile_limit.conf:
[Service]LimitNOFILE=65535
  1. Recarregue a configuração do daemon systemd e reinicie o NGINX:
$ systemctl daemon-reload$ systemctl restart nginx.service

Recursos adicionais

O SELinux é um recurso complexo e poderoso para gerenciar permissões do sistema operacional. Informações adicionais estão disponíveis nos documentos a seguir.


Compartilhar

Sobre o autor

Owen Garrett
Owen GarrettDiretor Sênior de Gestão de Produtos

Mais publicações de Owen Garrett

Publicações relacionadas no blog

Como o F5 NGINX One aprimora o gerenciamento e a observabilidade
NGINX | 10/10/2024

Como o F5 NGINX One aprimora o gerenciamento e a observabilidade

Descubra como o F5 NGINX One aprimora o gerenciamento e a observabilidade de ambientes híbridos e multicloud ao oferecer um console web centralizado baseado em SaaS, com insights em tempo real, conformidade automatizada e gerenciamento simplificado de configurações.

Do OpenAPI ao NGINX como gateway de API usando uma API declarativa
NGINX | 17/06/2024

Do OpenAPI ao NGINX como gateway de API usando uma API declarativa

Descubra como transformar uma definição de esquema OpenAPI em uma configuração NGINX totalmente funcional, operando como um API Gateway com a segurança de um Web Application Firewall e um Portal do Desenvolvedor, usando uma abordagem declarativa de API.

Como gerenciar picos repentinos de tráfego e sobrecarga do servidor
NGINX | 14/07/2021

Como gerenciar picos repentinos de tráfego e sobrecarga do servidor

Picos de tráfego e sobrecarga de servidores são preocupações sérias para varejistas online em qualquer época, mas especialmente durante a “Cyber Season” de compras de fim de ano. Detalhamos como você pode aproveitar as soluções de entrega de aplicações de nível de produção da NGINX para se preparar para aumentos repentinos de tráfego.

Distribuição segura de chaves privadas SSL com o NGINX
NGINX | 02/04/2019

Distribuição segura de chaves privadas SSL com o NGINX

Descrevemos três maneiras progressivamente mais seguras de proteger chaves privadas SSL ao configurar o NGINX para processar tráfego HTTPS: permitir acesso de leitura apenas ao usuário root, criptografar as chaves com senhas armazenadas separadamente e distribuir as senhas a partir de um repositório central.

Agrupando solicitações de API em lotes com NGINX Plus e o módulo JavaScript do NGINX
NGINX | 24/05/2018

Agrupando solicitações de API em lotes com NGINX Plus e o módulo JavaScript do NGINX

O módulo JavaScript do NGINX lançado com o NGINX Plus R15 oferece suporte a subsolicitações, possibilitando uma classe totalmente nova de casos de uso para o NGINX Plus. Este exemplo completo também usa a API do NGINX Plus e os módulos de chave-valor para converter solicitações de API dos clientes em solicitações separadas para as APIs dos microsserviços que compõem um aplicativo.

Como superar o esgotamento de portas efêmeras no NGINX e no NGINX Plus
NGINX | 19/04/2016

Como superar o esgotamento de portas efêmeras no NGINX e no NGINX Plus

Confira dicas para evitar o esgotamento de portas efêmeras com ajustes no kernel do Linux, conexões keepalive e conexões vinculadas dinamicamente a endereços IP locais.