Workshop RHEL 8 Beta: Construindo aplicações web funcionais

O RHEL 8 Beta oferece aos desenvolvedores muitos novos recursos, cuja listagem poderia ocupar várias páginas, mas aprender coisas novas é sempre melhor na prática. Portanto, abaixo, oferecemos um guia prático para criar uma infraestrutura de aplicativos real baseada no Red Hat Enterprise. Linux 8 Beta.

Workshop RHEL 8 Beta: Construindo aplicações web funcionais

Usaremos Python, uma linguagem de programação popular entre desenvolvedores, e uma combinação de Django e PostgreSQL, um framework de desenvolvimento de aplicações bastante comum, como base. Configuraremos o RHEL 8 Beta para funcionar com eles. Em seguida, adicionaremos mais alguns ingredientes (não classificados).

O ambiente de teste será alterado, pois é interessante explorar os recursos de automação, trabalhar com contêineres e experimentar ambientes com vários servidores. Para começar um novo projeto, você pode criar um protótipo pequeno e simples manualmente — dessa forma, você consegue ver exatamente o que precisa acontecer e como a interação funciona, e depois partir para a automação e a criação de configurações mais complexas. Hoje, falarei sobre como criar um protótipo desse tipo.

Vamos começar implantando uma imagem de máquina virtual (VM) do RHEL 8 Beta. Você pode instalar a máquina virtual do zero ou usar a imagem convidada KVM disponível com sua assinatura Beta. Ao usar a imagem convidada, você precisará configurar um CD virtual que conterá metadados e dados do usuário para inicialização na nuvem (cloud-init). Não é necessário fazer nada de especial com a estrutura de disco ou os pacotes disponíveis; qualquer configuração funcionará.

Vamos analisar todo o processo mais detalhadamente.

Instalando o Django

A versão mais recente do Django requer um ambiente virtual (virtualenv) com Python 3.5 ou posterior. As notas de lançamento da versão Beta indicam que o Python 3.6 está disponível, então vamos verificar se isso é verdade:

[cloud-user@8beta1 ~]$ python
-bash: python: command not found
[cloud-user@8beta1 ~]$ python3
-bash: python3: command not found

A Red Hat utiliza Python extensivamente como parte do conjunto de ferramentas do sistema no RHEL, então por que isso acontece?

O fato é que muitos desenvolvedores Python ainda estão considerando a atualização do Python 2 para o Python 2, enquanto o próprio Python 3 está em desenvolvimento ativo, com novas versões sendo lançadas constantemente. Portanto, para atender à necessidade de ferramentas de sistema estáveis ​​e, ao mesmo tempo, oferecer aos usuários acesso a várias novas versões do Python, o Python do sistema foi movido para um novo pacote, possibilitando a instalação tanto do Python 2.7 quanto do 3.6. Informações mais detalhadas sobre as mudanças e a justificativa por trás delas podem ser encontradas na publicação. Blog de Langdon White (Langdon White).

Portanto, para que o Python funcione, você só precisa instalar dois pacotes, sendo o python3-pip uma dependência.

sudo yum install python36 python3-virtualenv

Por que não usar chamadas diretas de módulos, como Langdon sugere, e instalar o pip3? Considerando a automação futura, sabe-se que o Ansible exige que o pip esteja instalado, pois o módulo pip não suporta ambientes virtuais (virtualenvs) com um executável pip personalizado.

Com um interpretador Python 3 funcionando, você pode continuar o processo de instalação do Django e obter um sistema operacional, juntamente com nossos outros componentes. Existem muitas implementações disponíveis online. Uma versão é apresentada aqui, mas os usuários são livres para usar seus próprios processos.

Vamos instalar as versões padrão do PostgreSQL e do Nginx disponíveis no RHEL 8 usando o Yum.

sudo yum install nginx postgresql-server

O PostgreSQL requer o psycopg2, mas só queremos que ele esteja disponível no ambiente virtual (virtualenv), então vamos instalá-lo usando o pip3 juntamente com o Django e o Gunicorn. Mas primeiro, precisamos configurar o ambiente virtual.

Há sempre muita discussão sobre o local correto para instalar projetos Django, mas, em caso de dúvida, você sempre pode consultar o padrão. Linux Padrão de Hierarquia do Sistema de Arquivos (FHS). Especificamente, o FHS afirma que /srv é usado para: "armazenar dados específicos do host — dados produzidos pelo sistema, como dados e scripts de servidores web, dados armazenados em servidores FTP e repositórios de sistemas de controle de versão (que apareceram no FHS-2.3 em 2004)".

Este é exatamente o nosso caso, então colocamos tudo o que precisamos em /srv, que pertence ao usuário do nosso aplicativo (cloud-user).

sudo mkdir /srv/djangoapp
sudo chown cloud-user:cloud-user /srv/djangoapp
cd /srv/djangoapp
virtualenv django
source django/bin/activate
pip3 install django gunicorn psycopg2
./django-admin startproject djangoapp /srv/djangoapp

Configurar o PostgreSQL e o Django é simples: crie um banco de dados, crie um usuário e configure as permissões. Um ponto importante a se considerar durante a instalação inicial do PostgreSQL é o script `postgresql-setup`, que é instalado junto com o pacote `postgresql-server`. Esse script auxilia na execução de tarefas básicas relacionadas à administração do cluster de banco de dados, como inicialização ou atualizações do cluster. Para configurar uma nova instância do PostgreSQL em um sistema RHEL, execute o seguinte comando:

sudo /usr/bin/postgresql-setup -initdb

Depois disso, você pode iniciar o PostgreSQL usando o systemd, criar um banco de dados e configurar o projeto no Django. Não se esqueça de reiniciar o PostgreSQL após fazer alterações no arquivo de configuração de autenticação do cliente (geralmente pg_hba.conf) para configurar o armazenamento de senhas para o usuário do aplicativo. Se você encontrar outros problemas, certifique-se de ter atualizado as configurações de IPv4 e IPv6 em pg_hba.conf.

systemctl enable -now postgresql

sudo -u postgres psql
postgres=# create database djangoapp;
postgres=# create user djangouser with password 'qwer4321';
postgres=# alter role djangouser set client_encoding to 'utf8';
postgres=# alter role djangouser set default_transaction_isolation to 'read committed';
postgres=# alter role djangouser set timezone to 'utc';
postgres=# grant all on DATABASE djangoapp to djangouser;
postgres=# q

No arquivo /var/lib/pgsql/data/pg_hba.conf:

# IPv4 local connections:
host    all        all 0.0.0.0/0                md5
# IPv6 local connections:
host    all        all ::1/128                 md5

No arquivo /srv/djangoapp/settings.py:

# Database
DATABASES = {
   'default': {
       'ENGINE': 'django.db.backends.postgresql_psycopg2',
       'NAME': '{{ db_name }}',
       'USER': '{{ db_user }}',
       'PASSWORD': '{{ db_password }}',
       'HOST': '{{ db_host }}',
   }
}

Após configurar o arquivo settings.py do projeto e definir a configuração do banco de dados, você pode executar o servidor de desenvolvimento para garantir que tudo esteja funcionando corretamente. Com o servidor de desenvolvimento em execução, é recomendável criar um usuário administrador para testar a conexão com o banco de dados.

./manage.py runserver 0.0.0.0:8000
./manage.py createsuperuser

WSGI? Wai?

Um servidor de desenvolvimento é útil para testes, mas para executar a aplicação, você precisa configurar o servidor e o proxy apropriados para a Interface de Gateway de Servidor Web (WSGI). Existem várias combinações comuns, como Apache HTTPD com uWSGI ou Nginx com Gunicorn.

A finalidade da Interface de Gateway do Servidor Web é encaminhar solicitações de servidor web Para o framework web Python. WSGI é uma relíquia de um passado terrível, quando os mecanismos CGI eram usados, e hoje o WSGI é um padrão de facto, independentemente do servidor web ou framework Python utilizado. Apesar de sua ampla adoção, ainda existem muitas nuances ao trabalhar com esses frameworks, e uma ampla gama de opções. Neste caso, tentaremos estabelecer comunicação entre Gunicorn e Nginx via socket.

Como ambos os componentes estão instalados no mesmo servidor, vamos tentar usar um socket UNIX em vez de um socket de rede. Já que um socket é necessário para a comunicação de qualquer forma, vamos um passo além e configurar a ativação de socket para o Gunicorn via systemd.

O processo de criação de serviços ativados por socket é bastante simples. Primeiro, crie um arquivo de unidade contendo uma diretiva ListenStream especificando o ponto onde o socket UNIX será criado. Em seguida, crie um arquivo de unidade para o serviço, no qual a diretiva Requires aponte para o arquivo de unidade do socket. Depois, no arquivo de unidade do serviço, basta chamar o Gunicorn a partir do ambiente virtual e criar um binding WSGI para o socket UNIX e a aplicação Django.

Aqui estão alguns exemplos de arquivos unitários que você pode usar como ponto de partida. Primeiro, vamos configurar o socket.

[Unit]
Description=Gunicorn WSGI socket

[Socket]
ListenStream=/run/gunicorn.sock

[Install]
WantedBy=sockets.target

Agora você precisa configurar o daemon Gunicorn.

[Unit]
Description=Gunicorn daemon
Requires=gunicorn.socket
After=network.target

[Service]
User=cloud-user
Group=cloud-user
WorkingDirectory=/srv/djangoapp

ExecStart=/srv/djangoapp/django/bin/gunicorn 
         —access-logfile - 
         —workers 3 
         —bind unix:gunicorn.sock djangoapp.wsgi

[Install]
WantedBy=multi-user.target

Para o Nginx, basta criar arquivos de configuração de proxy e configurar um diretório para armazenar conteúdo estático, caso utilize um. No RHEL, os arquivos de configuração do Nginx estão localizados em /etc/nginx/conf.d. Você pode copiar o exemplo a seguir para o arquivo /etc/nginx/conf.d/default.conf e iniciar o serviço. Certifique-se de especificar o server_name de acordo com o nome do seu host.

server {
   listen 80;
   server_name 8beta1.example.com;

   location = /favicon.ico { access_log off; log_not_found off; }
   location /static/ {
       root /srv/djangoapp;
   }

   location / {
       proxy_set_header Host $http_host;
       proxy_set_header X-Real-IP $remote_addr;
       proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
       proxy_set_header X-Forwarded-Proto $scheme;
       proxy_pass http://unix:/run/gunicorn.sock;
   }
}

Inicie o socket do Gunicorn e o Nginx usando o systemd e você estará pronto para testar.

Erro de gateway inválido?

Se você inserir o endereço em um navegador, provavelmente receberá um erro 502 Bad Gateway. Isso pode ser causado por permissões configuradas incorretamente no socket UNIX ou por problemas mais complexos relacionados ao controle de acesso no SE.Linux.

No log de erros do nginx, você pode ver uma linha como esta:

2018/12/18 15:38:03 [crit] 12734#0: *3 connect() to unix:/run/gunicorn.sock failed (13: Permission denied) while connecting to upstream, client: 192.168.122.1, server: 8beta1.example.com, request: "GET / HTTP/1.1", upstream: "http://unix:/run/gunicorn.sock:/", host: "8beta1.example.com"

Se testarmos o Gunicorn diretamente, obteremos uma resposta vazia.

curl —unix-socket /run/gunicorn.sock 8beta1.example.com

Vamos descobrir por que isso está acontecendo. Se você abrir o registro, provavelmente verá que o problema está relacionado ao SE.LinuxComo estamos executando um daemon que não possui sua própria política, ele é marcado como init_t. Vamos testar essa teoria na prática.

sudo setenforce 0

Tudo isso pode provocar críticas e até lágrimas, mas estamos apenas depurando um protótipo. Vamos desativar a verificação só para garantir que esse é o problema e depois voltaremos tudo ao normal.

Ao atualizar a página no seu navegador ou executar novamente o comando curl, você poderá visualizar a página de teste do Django.

Então, depois de garantir que tudo funciona e que não há mais problemas com permissões, ligamos o SE novamente.Linux.

sudo setenforce 1

Este artigo não abordará o audit2allow nem a criação de políticas baseadas em alertas usando o sepolgen, pois atualmente não há uma aplicação Django real, portanto não existe um mapeamento completo do que o Gunicorn pode querer acessar e o que deve ser negado. Sendo assim, é importante manter o SE em execução.Linux Para proteger o sistema, permitindo ao mesmo tempo que a aplicação seja executada e registre mensagens no log de auditoria, de forma que políticas reais possam ser criadas com base nelas.

Especificando domínios permissivos

Sobre domínios permitidos no SELinux Nem todos já ouviram falar delas, mas não são nenhuma novidade. Muitos até já trabalharam com elas sem se darem conta. Quando uma política é criada com base em mensagens de auditoria, a política resultante representa um domínio permitido. Vamos tentar criar uma política de permissão simples.

Para criar um domínio específico permitido para o Gunicorn, é necessária uma política e os arquivos relevantes precisam ser marcados. Também são necessárias ferramentas para compilar as novas políticas.

sudo yum install selinux-policy-devel

O mecanismo de lista de permissões é uma ótima ferramenta para identificar problemas, especialmente ao lidar com aplicativos personalizados ou aplicativos que são distribuídos sem políticas predefinidas. Neste caso, a política de lista de permissões para o Gunicorn será o mais simples possível: declaramos o tipo principal (gunicorn_t), declaramos o tipo que usaremos para marcar vários arquivos executáveis ​​(gunicorn_exec_t) e, em seguida, configuramos uma transição para que o sistema marque corretamente os processos em execução. A última linha define a política para ser permitida por padrão quando o aplicativo for carregado.

gunicorn.te:

policy_module(gunicorn, 1.0)

type gunicorn_t;
type gunicorn_exec_t;
init_daemon_domain(gunicorn_t, gunicorn_exec_t)
permissive gunicorn_t;

Você pode compilar este arquivo de política e adicioná-lo ao sistema.

make -f /usr/share/selinux/devel/Makefile
sudo semodule -i gunicorn.pp

sudo semanage permissive -a gunicorn_t
sudo semodule -l | grep permissive

Vamos verificar se o SE está bloqueandoLinux algo além daquilo a que o nosso demônio desconhecido se refere.

sudo ausearch -m AVC

type=AVC msg=audit(1545315977.237:1273): avc:  denied { write } for pid=19400 comm="nginx" name="gunicorn.sock" dev="tmpfs" ino=52977 scontext=system_u:system_r:httpd_t:s0 tcontext=system_u:object_r:var_run_t:s0 tclass=sock_file permissive=0

SELinux Impede que o Nginx escreva dados no socket UNIX usado pelo Gunicorn. Normalmente, as políticas são alteradas nesses casos, mas outros problemas ainda precisam ser resolvidos. Você também pode alterar as configurações de domínio, convertendo-o de um domínio restrito para um domínio permissivo. Agora, vamos mover o httpd_t para o domínio permissivo. Isso fornecerá ao Nginx o acesso necessário e poderemos continuar a depuração.

sudo semanage permissive -a httpd_t

Então, quando você conseguiu salvar a proteção SE?Linux (Na verdade, você não deveria deixar o projeto com a SE)Linux Se o sistema estiver em modo restrito e os domínios de permissão estiverem sendo carregados, precisamos descobrir exatamente o que precisa ser marcado como gunicorn_exec_t para que tudo volte a funcionar corretamente. Vamos tentar acessar o site para verificar se há novas mensagens sobre restrições de acesso.

sudo ausearch -m AVC -c gunicorn

Você pode ver muitas mensagens contendo 'comm="gunicorn"' que executam várias ações em arquivos em /srv/djangoapp, então esse é obviamente um dos comandos que vale a pena sinalizar.

Mas, além disso, aparece uma mensagem como esta:

type=AVC msg=audit(1545320700.070:1542): avc:  denied { execute } for pid=20704 comm="(gunicorn)" name="python3.6" dev="vda3" ino=8515706 scontext=system_u:system_r:init_t:s0 tcontext=unconfined_u:object_r:var_t:s0 tclass=file permissive=0

Se você verificar o status do serviço gunicorn ou executar o comando ps, não verá nenhum processo em execução. Parece que o gunicorn está tentando acessar o interpretador Python em nosso ambiente virtual, possivelmente para executar scripts de trabalho. Então, vamos marcar esses dois executáveis ​​agora e ver se conseguimos abrir nossa página de teste do Django.

chcon -t gunicorn_exec_t /srv/djangoapp/django/bin/gunicorn /srv/djangoapp/django/bin/python3.6

Você precisará reiniciar o serviço gunicorn para selecionar um novo rótulo. Você pode reiniciá-lo imediatamente ou parar o serviço e deixar o socket iniciar quando o site for aberto no navegador. Verifique se os processos receberam os rótulos desejados usando o comando ps.

ps -efZ | grep gunicorn

Não se esqueça de criar uma política SE normal mais tarde.Linux!

Se você observar as mensagens AVC agora, a última mensagem contém `permissive=1` para tudo relacionado à aplicação e `permissive=0` para o restante do sistema. Compreender o acesso específico necessário para a aplicação em si ajudará você a encontrar a solução ideal para esses problemas mais rapidamente. Mas, até lá, é melhor manter o sistema seguro e ter um registro de auditoria claro e utilizável para o seu projeto Django.

sudo ausearch -m AVC

Acabou!

Agora temos um projeto Django funcional com um frontend construído em Nginx e Gunicorn WSGI. Configuramos o Python 3 e o PostgreSQL 10 a partir dos repositórios do RHEL 8 Beta. Agora podemos prosseguir e criar (ou simplesmente implantar) aplicações Django ou explorar outras ferramentas disponíveis no RHEL 8 Beta para automatizar o processo de configuração, melhorar o desempenho ou até mesmo conteinerizar essa configuração.

Fonte: habr.com

Compre hospedagem confiável para sites com proteção DDoS, servidores VPS VDS 🔥 Compre hospedagem de sites confiável com proteção contra DDoS, servidores VPS/VDS | ProHoster