Workshop RHEL 8 Beta: Criando aplicativos da Web ao vivo

O RHEL 8 Beta oferece aos desenvolvedores muitos recursos novos, cuja lista pode levar páginas; no entanto, é sempre melhor aprender coisas novas na prática; portanto, sugerimos que você participe de um workshop para criar uma infraestrutura de aplicativos baseada no Red Hat Enterprise Linux 8 Beta.



Vamos pegar o Python, uma combinação do Django e PostgreSQL, um pacote bastante comum para a criação de aplicativos, e configurar o RHEL 8 Beta para trabalhar com eles. Em seguida, adicione mais alguns ingredientes (não classificados).

O ambiente de teste mudará, porque é interessante estudar os recursos de automação, trabalhar com contêineres e experimentar ambientes com vários servidores. Para começar a trabalhar com um novo projeto, você pode começar criando um pequeno protótipo simples manualmente - dessa forma, você pode ver o que exatamente precisa acontecer e como a interação é realizada e, em seguida, passar para a automação e criar configurações mais complexas. Hoje é a história de criar esse protótipo.

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

Vejamos todo o processo com mais detalhes.

Instale o Django


Com a versão mais recente do Django, você precisará de um ambiente virtual (virtualenv) com o Python 3.5 ou posterior. Nas notas para Beta, você pode ver que o Python 3.6 está disponível, 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 usa ativamente o Python como um kit de ferramentas do sistema no RHEL, então por que você obtém esse resultado?

O fato é que muitos desenvolvedores que usam o Python ainda estão pensando em mudar do Python 2 para o Python 3, enquanto o próprio Python 3 está em desenvolvimento ativo, e cada vez mais novas versões estão aparecendo constantemente. Portanto, para satisfazer a necessidade de ferramentas estáveis ​​do sistema e, ao mesmo tempo, oferecer aos usuários acesso a várias novas versões do Python, o Python do sistema foi transferido para um novo pacote e forneceu a capacidade de instalar o Python 2.7 e 3.6. Mais informações sobre as mudanças e por que isso foi feito podem ser encontradas na postagem do blog de Langdon White .

Portanto, para começar a trabalhar com o Python, você precisa instalar apenas dois pacotes, enquanto o python3-pip será exibido como uma dependência.

 sudo yum install python36 python3-virtualenv 

Por que não usar chamadas diretas de módulo, como sugere Langdon, e não instalar o pip3? Tendo em vista a automação futura, é sabido que o Ansible exigirá o pip instalado, pois o módulo pip não suporta ambientes virtuais (virtualenvs) com um executável de pip personalizado.

Com um intérprete python3 funcional à sua disposição, você pode continuar o processo de instalação do Django e obter um sistema funcional junto com nossos outros componentes. A rede apresenta muitas opções para implementação. Uma versão é apresentada aqui, mas os usuários podem usar seus próprios processos.

As versões do PostgreSQL e Nginx disponíveis no RHEL 8 serão instaladas por padrão usando o Yum.

 sudo yum install nginx postgresql-server 

O PostgreSQL exigirá o psycopg2, mas ele deve estar disponível apenas no ambiente virtualenv, portanto, o instalaremos usando o pip3 junto com o Django e o Gunicorn. Mas primeiro precisamos configurar o virtualenv.

Sempre há muito debate sobre a escolha do local certo para instalar os projetos do Django, mas em caso de dúvida, você sempre pode recorrer ao Linux Filesystem Hierarchy Standard. Em particular, a ESF diz que / srv é usado para: “armazenar dados específicos do host - dados que o sistema fornece, por exemplo, dados e scripts de servidores da Web, dados armazenados em servidores FTP, bem como repositórios de sistemas de controle versões (introduzidas na FHS-2.3 em 2004). ”

Este é apenas o nosso caso, por isso colocamos tudo o que precisamos em / srv, que pertence ao nosso usuário do aplicativo (usuário da nuvem).

 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 

A configuração do PostgreSQL e do Django é simples: crie um banco de dados, crie um usuário, configure permissões. Há um ponto a ser lembrado ao instalar o PostgreSQL pela primeira vez - o script postgresql-setup, instalado com o pacote postgresql-server. Esse script ajuda a executar tarefas básicas relacionadas à administração de um cluster de banco de dados, como inicializar um cluster ou atualizar o processo. Para configurar uma nova instância do PostgreSQL no sistema RHEL, precisamos executar o comando:

 sudo /usr/bin/postgresql-setup -initdb 

Depois disso, você pode iniciar o PostgreSQL usando systemd, criar um banco de dados e configurar o projeto no Django. Lembre-se de reiniciar o PostgreSQL depois de 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 outras dificuldades, verifique se as configurações de IPv4 e IPv6 no arquivo pg_hba.conf foram alteradas.

 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 }}', } } 

Depois de configurar o arquivo settings.py no projeto e definir a configuração do banco de dados, você pode iniciar o servidor de desenvolvimento para garantir que tudo funcione. Após iniciar o servidor de desenvolvimento, é bom 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


O servidor de desenvolvimento é útil para teste, mas para executar o aplicativo, você deve configurar o servidor e o proxy apropriados para a WSGI (Web Server Gateway Interface). Existem vários pacotes configuráveis ​​comuns, por exemplo, Apache HTTPD com uWSGI ou Nginx com Gunicorn.

O objetivo da interface do gateway do servidor da Web é redirecionar solicitações do servidor da web para a estrutura da web do Python. O WSGI é um tipo de legado do passado terrível quando os mecanismos CGI estavam em uso, e hoje o WSGI é realmente o padrão, independentemente do servidor da Web ou da estrutura Python usada. Mas, apesar de sua ampla distribuição, ainda existem muitas nuances ao trabalhar com essas estruturas e muitas opções. Neste caso, tentaremos estabelecer uma interação entre Gunicorn e Nginx através do soquete.

Como esses dois componentes estão instalados no mesmo servidor, tentaremos usar um soquete UNIX em vez de um soquete de rede. Como a comunicação requer um soquete de qualquer maneira, vamos tentar mais uma etapa e configurar a ativação do soquete para o Gunicorn através do systemd.

O processo de criação de serviços ativados por soquete é bastante simples. Primeiro, é criado um arquivo de unidade que contém a diretiva ListenStream, apontando para o ponto em que o soquete UNIX será criado, depois um arquivo de unidade para o serviço, onde a diretiva Requer apontará para o arquivo de unidade de soquete. Então, no arquivo de unidade do serviço, resta apenas chamar o Gunicorn do ambiente virtual e criar uma ligação WSGI para o soquete UNIX e o aplicativo Django.

Aqui estão alguns exemplos de arquivos de unidade que podem ser tomados como base. Primeiro, configure o soquete.

 [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 um arquivo de configuração de proxy e configurar um diretório para armazenar conteúdo estático, se você o usar. No RHEL, os arquivos de configuração do Nginx são /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 server_name de acordo com o nome do 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; } } 

Execute os soquetes Gunicorn e Nginx usando systemd e você poderá iniciar o teste.

Erro de gateway incorreto?


Se você digitar o endereço no navegador, provavelmente receberá o erro 502 Bad Gateway. Isso pode ser causado por permissões configuradas incorretamente para o soquete UNIX ou devido a problemas mais complexos relacionados ao controle de acesso no SELinux.

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 ver por que isso está acontecendo. Se você abrir o log, provavelmente veremos que o problema está relacionado ao SELinux. Como estamos executando um daemon para o qual não criamos nossa própria política, ele é marcado como init_t. Vamos testar esta teoria na prática.

 sudo setenforce 0 

Tudo isso pode causar críticas e lágrimas de sangue, mas isso está apenas depurando o protótipo. Desativamos a verificação apenas para garantir que esse seja o problema, após o qual retornaremos tudo de volta aos seus lugares.

Atualizando a página no navegador ou reiniciando nosso comando curl, você pode ver a página de teste do Django.

Portanto, garantindo que tudo funcione e que não haja mais problemas de permissões, reativamos o SELinux.

 sudo setenforce 1 

Não se fala em audit2allow e na criação de políticas baseadas em alertas usando o sepolgen, já que atualmente não existe um aplicativo Django real, não há um mapa completo do que o Gunicorn pode querer acessar e que acesso deve ser negado. Portanto, é necessário preservar o SELinux para proteger o sistema e, ao mesmo tempo, permitir que o aplicativo inicie e deixe mensagens no log de auditoria para que você possa criar uma política real com base neles.

Especificando domínios permitidos


Nem todo mundo já ouviu falar de domínios permitidos no SELinux, mas não há nada de novo neles. Muitos até trabalharam com eles, sem perceberem eles mesmos. Quando uma política é criada com base em mensagens de auditoria, a política criada é um domínio permitido. Vamos tentar criar a política permissiva mais simples.

Para criar um domínio permitido específico para o Gunicorn, você precisa de uma determinada política e também deve marcar os arquivos apropriados. Além disso, são necessárias ferramentas para montar novas políticas.

 sudo yum install selinux-policy-devel 

O mecanismo de domínio resolvido é uma ótima ferramenta para identificar problemas, especialmente quando se trata de um aplicativo personalizado ou de aplicativos que vêm sem políticas já criadas. Nesse caso, a política de domínio permitida para o Gunicorn será a mais simples possível - declare o tipo principal (gunicorn_t), declare o tipo que usaremos para marcar vários arquivos executáveis ​​(gunicorn_exec_t) e, em seguida, configure a transição para o sistema para marcar corretamente os processos em execução . A última linha define a política como habilitada por padrão no momento do carregamento.

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 esse arquivo de políticas 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 SELinux está bloqueando outra coisa além do que nosso daemon desconhecido está acessando.

 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 

O SELinux impede que o Nginx grave dados no soquete UNIX usado pelo Gunicorn. Normalmente, nesses casos, os políticos começam a mudar, mas há outras tarefas pela frente. Você também pode alterar as configurações do domínio, transformando-o de um domínio de restrição em um domínio de permissão. Agora mova httpd_t para o domínio de permissão. Isso fornecerá ao Nginx o acesso necessário e poderemos continuar trabalhando na depuração.

 sudo semanage permissive -a httpd_t 

Portanto, quando foi possível preservar a proteção do SELinux (na verdade, você não deve deixar o projeto com o SELinux no modo de restrição) e os domínios de permissão são carregados, é necessário descobrir o que exatamente precisa ser marcado como gunicorn_exec_t para que tudo funcione novamente como esperado. Vamos tentar acessar o site para ver 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 nos arquivos em / srv / djangoapp, portanto, obviamente, este é apenas um dos comandos que você deve marcar.

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ê observar o status do serviço gunicorn ou executar o comando ps, nenhum processo em execução será exibido. O Gunicorn parece estar tentando acessar o interpretador Python em nosso ambiente virtualenv, possivelmente para executar scripts de trabalho (trabalhadores). Então agora vamos marcar esses dois executáveis ​​e ver se podemos 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 poder selecionar um novo rótulo. Você pode reiniciá-lo imediatamente ou interromper o serviço e permitir que o soquete inicie-o quando você abrir o site em um navegador. Verifique se os processos obtêm os rótulos corretos usando ps.

 ps -efZ | grep gunicorn 

Lembre-se de criar uma política normal do SELinux posteriormente!

Se você observar as mensagens do AVC agora, a última mensagem conterá permissivo = 1 para tudo relacionado ao aplicativo e permissivo = 0 para o restante do sistema. Se você entender que tipo de acesso um aplicativo real precisa, poderá encontrar rapidamente a melhor maneira de resolver esses problemas. Mas até então, é melhor que o sistema esteja protegido e para obter uma auditoria clara e utilizável pelo projeto Django.

 sudo ausearch -m AVC 

Acabou!


Um projeto Django em funcionamento com um frontend no Nginx e Gunicorn WSGI apareceu. Configuramos o Python 3 e o PostgreSQL 10 a partir dos repositórios RHEL 8 Beta. Agora você pode seguir em frente e criar (ou apenas implantar) aplicativos Django ou explorar outras ferramentas disponíveis no RHEL 8 Beta para automatizar o processo de ajuste, melhorar o desempenho ou até conter essa configuração.

Source: https://habr.com/ru/post/pt447540/


All Articles