Taller RHEL 8 Beta: Creación de aplicacións web que funcionen

RHEL 8 Beta ofrece aos desenvolvedores moitas funcións novas, cuxa listaxe podería ocupar páxinas, non obstante, aprender cousas novas sempre é mellor na práctica, polo que a continuación ofrecemos un obradoiro sobre a creación dunha infraestrutura de aplicacións baseada en Red Hat Enterprise Linux 8 Beta.

Taller RHEL 8 Beta: Creación de aplicacións web que funcionen

Tomemos como base Python, unha linguaxe de programación popular entre os desenvolvedores, unha combinación de Django e PostgreSQL, unha combinación bastante común para crear aplicacións, e configuremos RHEL 8 Beta para traballar con eles. Despois engadiremos un par de ingredientes máis (sen clasificar).

O ambiente de proba cambiará, porque é interesante explorar as posibilidades da automatización, traballar con contedores e probar contornas con varios servidores. Para comezar cun novo proxecto, podes comezar creando un prototipo pequeno e sinxelo a man para que poidas ver exactamente o que ten que pasar e como interactúa, e despois pasar a automatizar e crear configuracións máis complexas. Hoxe falamos da creación deste prototipo.

Comecemos por implementar a imaxe RHEL 8 Beta VM. Podes instalar unha máquina virtual desde cero ou usar a imaxe de convidado KVM dispoñible coa túa subscrición beta. Cando use unha imaxe de convidado, terá que configurar un CD virtual que conterá metadatos e datos de usuario para a inicialización na nube (cloud-init). Non precisa facer nada especial coa estrutura do disco ou os paquetes dispoñibles; calquera configuración servirá.

Vexamos máis de cerca todo o proceso.

Instalación de Django

Coa versión máis recente de Django, necesitarás un ambiente virtual (virtualenv) con Python 3.5 ou posterior. Nas notas beta podes ver que Python 3.6 está dispoñible, imos comprobar se este é realmente o caso:

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

Red Hat usa activamente Python como un kit de ferramentas do sistema en RHEL, entón por que resulta isto?

O caso é que moitos desenvolvedores de Python aínda están contemplando a transición de Python 2 a Python 2, mentres que o propio Python 3 está en desenvolvemento activo, e cada vez aparecen máis novas versións. Polo tanto, para satisfacer a necesidade de ferramentas de sistema estables ao tempo que se lles ofrece aos usuarios acceso a varias novas versións de Python, o sistema Python trasladouse a un novo paquete e proporcionou a posibilidade de instalar Python 2.7 e 3.6. Pódese atopar máis información sobre os cambios e por que se fixeron na publicación en Blog de Langdon White (Langdon Branco).

Entón, para que Python funcione, só precisa instalar dous paquetes, con python3-pip incluído como dependencia.

sudo yum install python36 python3-virtualenv

Por que non usar chamadas directas ao módulo como suxire Langdon e instalar pip3? Tendo en conta a próxima automatización, sábese que Ansible requirirá a instalación de pip para executarse, xa que o módulo pip non admite virtualenvs cun executable pip personalizado.

Cun intérprete Python3 que funcione á túa disposición, podes continuar co proceso de instalación de Django e ter un sistema que funcione xunto cos outros compoñentes. Hai moitas opcións de implementación dispoñibles en Internet. Hai unha versión presentada aquí, pero os usuarios poden usar os seus propios procesos.

Instalaremos as versións PostgreSQL e Nginx dispoñibles en RHEL 8 por defecto usando Yum.

sudo yum install nginx postgresql-server

PostgreSQL requirirá psycopg2, pero só ten que estar dispoñible nun ambiente virtualenv, polo que instalarémolo usando pip3 xunto con Django e Gunicorn. Pero primeiro necesitamos configurar virtualenv.

Sempre hai moito debate sobre o tema da elección do lugar axeitado para instalar os proxectos de Django, pero en caso de dúbida, sempre podes acudir ao estándar de xerarquía do sistema de ficheiros de Linux. En concreto, o FHS di que /srv úsase para: "almacenar datos específicos do host: datos que produce o sistema, como datos e scripts do servidor web, datos almacenados en servidores FTP e repositorios do sistema de control". -2.3 en 2004).

Este é exactamente o noso caso, polo que poñemos todo o que necesitamos en /srv, que é propiedade do usuario da nosa aplicación (usuario da nube).

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 PostgreSQL e Django é sinxelo: crear unha base de datos, crear un usuario, configurar permisos. Unha cousa a ter en conta cando se instala inicialmente PostgreSQL é o script postgresql-setup que se instala co paquete postgresql-server. Este script axúdache a realizar tarefas básicas asociadas á administración do clúster de bases de datos, como a inicialización do clúster ou o proceso de actualización. Para configurar unha nova instancia de PostgreSQL nun sistema RHEL, necesitamos executar o comando:

sudo /usr/bin/postgresql-setup -initdb

Despois pode iniciar PostgreSQL usando systemd, crear unha base de datos e configurar un proxecto en Django. Lembra reiniciar PostgreSQL despois de facer cambios no ficheiro de configuración de autenticación do cliente (normalmente pg_hba.conf) para configurar o almacenamento do contrasinal para o usuario da aplicación. Se atopas outras dificultades, asegúrate de cambiar a configuración de IPv4 e IPv6 no ficheiro 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 ficheiro /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 ficheiro /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 }}',
   }
}

Despois de configurar o ficheiro settings.py no proxecto e configurar a base de datos, pode iniciar o servidor de desenvolvemento para asegurarse de que todo funciona. Despois de iniciar o servidor de desenvolvemento, é unha boa idea crear un usuario administrador para probar a conexión coa base de datos.

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

WSGI? Wai?

O servidor de desenvolvemento é útil para probar, pero para executar a aplicación debes configurar o servidor e o proxy axeitados para a Interface de pasarela do servidor web (WSGI). Hai varias combinacións comúns, por exemplo, Apache HTTPD con uWSGI ou Nginx con Gunicorn.

O traballo da interface de pasarela do servidor web é reenviar as solicitudes do servidor web ao marco web de Python. WSGI é unha reliquia do terrible pasado cando existían os motores CGI, e hoxe WSGI é o estándar de facto, independentemente do servidor web ou do framework Python utilizado. Pero a pesar do seu uso xeneralizado, aínda hai moitos matices ao traballar con estes marcos e moitas opcións. Neste caso, tentaremos establecer a interacción entre Gunicorn e Nginx a través dun socket.

Dado que ambos os compoñentes están instalados no mesmo servidor, intentemos usar un socket UNIX en lugar dun socket de rede. Xa que a comunicación require un socket en calquera caso, intentemos dar un paso máis e configurar a activación do socket para Gunicorn a través de systemd.

O proceso de creación de servizos activados por socket é bastante sinxelo. En primeiro lugar, créase un ficheiro de unidade que contén unha directiva ListenStream que apunta ao punto no que se creará o socket UNIX, despois un ficheiro de unidade para o servizo no que a directiva Requires apuntará ao ficheiro de unidade de socket. Despois, no ficheiro da unidade de servizo, só queda chamar a Gunicorn desde o entorno virtual e crear unha ligazón WSGI para o socket UNIX e a aplicación Django.

Aquí tes algúns exemplos de ficheiros unitarios que podes usar como base. Primeiro configuramos o socket.

[Unit]
Description=Gunicorn WSGI socket

[Socket]
ListenStream=/run/gunicorn.sock

[Install]
WantedBy=sockets.target

Agora cómpre 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 Nginx, é unha cuestión sinxela de crear ficheiros de configuración de proxy e configurar un directorio para almacenar contido estático se está a usar un. En RHEL, os ficheiros de configuración de Nginx atópanse en /etc/nginx/conf.d. Pode copiar o seguinte exemplo no ficheiro /etc/nginx/conf.d/default.conf e iniciar o servizo. Asegúrate de configurar o nome do servidor para que coincida co teu nome de 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;
   }
}

Inicia o socket Gunicorn e Nginx usando systemd e xa estás listo para comezar a probar.

Erro de pasarela incorrecta?

Se introduces o enderezo no teu navegador, probablemente recibirás un erro 502 Bad Gateway. Pode ser causado por permisos de socket UNIX configurados incorrectamente, ou pode deberse a problemas máis complexos relacionados co control de acceso en SELinux.

No rexistro de erros de nginx podes ver unha liña 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 probamos Gunicorn directamente, obteremos unha resposta baleira.

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

Imos descubrir por que ocorre isto. Se abres o rexistro, probablemente verás que o problema está relacionado con SELinux. Dado que estamos executando un daemon para o que non se creou ningunha política, está marcado como init_t. Imos probar esta teoría na práctica.

sudo setenforce 0

Todo isto pode causar críticas e bágoas de sangue, pero isto é só depurar o prototipo. Desactivemos a comprobación só para asegurarnos de que este é o problema, despois de que devolveremos todo ao seu lugar.

Ao actualizar a páxina no navegador ou volver executar o noso comando curl, podes ver a páxina de proba de Django.

Entón, despois de asegurarnos de que todo funciona e non hai máis problemas de permisos, volvemos a activar SELinux.

sudo setenforce 1

Non falarei aquí de audit2allow nin de crear políticas baseadas en alertas con sepolgen, xa que non hai ningunha aplicación real de Django polo momento, polo que non hai un mapa completo do que Gunicorn pode querer acceder e ao que debería denegar o acceso. Polo tanto, é necesario manter SELinux en execución para protexer o sistema, ao mesmo tempo que permite que a aplicación se execute e deixe mensaxes no rexistro de auditoría para que a política real poida ser creada a partir delas.

Especificación de dominios permisivos

Non todos escoitaron falar dos dominios permitidos en SELinux, pero non son nada novo. Moitos mesmo traballaron con eles sen sequera darse conta. Cando se crea unha política baseada en mensaxes de auditoría, a política creada representa o dominio resolto. Tentemos crear unha política de permisos sinxela.

Para crear un dominio permitido específico para Gunicorn, necesitas algún tipo de política e tamén tes que marcar os ficheiros axeitados. Ademais, son necesarias ferramentas para montar novas políticas.

sudo yum install selinux-policy-devel

O mecanismo de dominios permitidos é unha excelente ferramenta para identificar problemas, especialmente cando se trata dunha aplicación personalizada ou aplicacións que se envían sen políticas xa creadas. Neste caso, a política de dominio permitido para Gunicorn será o máis sinxela posible: declare un tipo principal (gunicorn_t), declare un tipo que usaremos para marcar varios executables (gunicorn_exec_t) e despois configure unha transición para que o sistema marque correctamente procesos en execución. A última liña establece a política como activada por defecto no momento en que se carga.

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;

Podes compilar este ficheiro de políticas e engadilo ao teu sistema.

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

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

Comprobamos se SELinux está bloqueando outra cousa que non sexa a que accede o noso daemon descoñecido.

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 impide que Nginx escriba datos no socket UNIX usado por Gunicorn. Normalmente, nestes casos, as políticas comezan a cambiar, pero hai outros retos por diante. Tamén pode cambiar a configuración do dominio dun dominio de restrición a un dominio de permisos. Agora imos mover httpd_t ao dominio de permisos. Isto dará a Nginx o acceso necesario e poderemos continuar co traballo de depuración.

sudo semanage permissive -a httpd_t

Entón, unha vez que conseguiu manter SELinux protexido (realmente non debería deixar un proxecto SELinux en modo restrinxido) e os dominios de permisos están cargados, cómpre descubrir o que se debe marcar exactamente como gunicorn_exec_t para que todo funcione correctamente. de novo. Tentemos visitar o sitio web para ver novas mensaxes sobre restricións de acceso.

sudo ausearch -m AVC -c gunicorn

Verás moitas mensaxes que conteñen 'comm="gunicorn"' que fan varias cousas en ficheiros en /srv/djangoapp, polo que este é, obviamente, un dos comandos que paga a pena marcar.

Pero ademais, aparece unha mensaxe 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 miras o estado do servizo gunicorn ou executas o comando ps, non verás ningún proceso en execución. Parece que Gunicorn está tentando acceder ao intérprete de Python no noso entorno virtualenv, posiblemente para executar scripts de traballo. Entón, agora marquemos estes dous ficheiros executables e comprobemos se podemos abrir a nosa páxina de proba de Django.

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

O servizo de gunicorn terá que reiniciarse antes de poder seleccionar a nova etiqueta. Pode reinicialo inmediatamente ou deter o servizo e deixar que o socket o inicie cando abra o sitio no navegador. Verifique que os procesos recibiron as etiquetas correctas usando ps.

ps -efZ | grep gunicorn

Non esquezas crear unha política SELinux normal máis tarde!

Se miras agora as mensaxes AVC, a última mensaxe contén permissive=1 para todo o relacionado coa aplicación e permissive=0 para o resto do sistema. Se entendes que tipo de acceso necesita unha aplicación real, podes atopar rapidamente a mellor forma de resolver estes problemas. Pero ata entón, o mellor é manter o sistema seguro e obter unha auditoría clara e utilizable do proxecto Django.

sudo ausearch -m AVC

Pasou!

Apareceu un proxecto de Django en funcionamento cun frontend baseado en Nginx e Gunicorn WSGI. Configuramos Python 3 e PostgreSQL 10 desde os repositorios RHEL 8 Beta. Agora podes avanzar e crear (ou simplemente implantar) aplicacións de Django ou explorar outras ferramentas dispoñibles en RHEL 8 Beta para automatizar o proceso de configuración, mellorar o rendemento ou incluso contener esta configuración.

Fonte: www.habr.com

Engadir un comentario