RHEL 8 Beta ofrece a los desarrolladores muchas características nuevas, la lista de las cuales puede tomar páginas, sin embargo, siempre es mejor aprender cosas nuevas en la práctica, por lo tanto, sugerimos pasar por un taller para crear una infraestructura de aplicaciones basada en Red Hat Enterprise Linux 8 Beta.

Tomamos Python, una combinación de Django y PostgreSQL, un paquete bastante común para crear aplicaciones, y configuramos RHEL 8 Beta para trabajar con ellos. Luego agregue un par de ingredientes más (sin clasificar).
El entorno de prueba cambiará, porque es interesante estudiar las capacidades de automatización, trabajar con contenedores y probar entornos con múltiples servidores. Para comenzar con un nuevo proyecto, puede comenzar creando un pequeño prototipo simple de forma manual, de esta manera puede ver exactamente qué debe suceder y cómo se lleva a cabo la interacción, y luego pasar a la automatización y crear configuraciones más complejas. Hoy es la historia de crear tal prototipo.
Comencemos desplegando la imagen de la máquina virtual RHEL 8 Beta VM. Puede instalar la máquina virtual desde cero o usar la imagen de invitado KVM, disponible con una suscripción Beta. Cuando utilice una imagen de invitado, deberá configurar un CD virtual, que contendrá metadatos y datos de usuario para la inicialización de la nube (cloud-init). No necesita hacer nada especial con la estructura del disco o los paquetes disponibles, cualquier configuración servirá.
Veamos todo el proceso con más detalle.
Instalar Django
Con la última versión de Django, necesitará un entorno virtual (virtualenv) con Python 3.5 o posterior. En las notas para Beta, puede ver que Python 3.6 está disponible, verifiquemos si esto es cierto:
[cloud-user@8beta1 ~]$ python -bash: python: command not found [cloud-user@8beta1 ~]$ python3 -bash: python3: command not found
Red Hat utiliza activamente Python como un kit de herramientas del sistema en RHEL, entonces, ¿por qué obtiene este resultado?
El hecho es que muchos desarrolladores que usan Python todavía están pensando en cambiar de Python 2 a Python 3, mientras que Python 3 está en desarrollo activo, y cada vez aparecen más versiones nuevas. Por lo tanto, para satisfacer la necesidad de herramientas del sistema estables y, al mismo tiempo, ofrecer a los usuarios acceso a varias versiones nuevas de Python, el sistema Python se transfirió a un nuevo paquete y proporcionó la capacidad de instalar Python 2.7 y 3.6. Puede encontrar más información sobre los cambios y por qué se hizo esto en la
publicación del blog Langdon White .
Entonces, para que Python funcione, solo necesita instalar dos paquetes, mientras que python3-pip se abrirá como una dependencia.
sudo yum install python36 python3-virtualenv
¿Por qué no utilizar llamadas directas de módulos, como sugiere Langdon, y no instalar pip3? Teniendo en cuenta la próxima automatización, se sabe que Ansible requerirá pip instalado, ya que el módulo pip no admite entornos virtuales (virtualenvs) con un ejecutable de pip personalizado.
Con un intérprete de python3 en funcionamiento a su disposición, puede continuar el proceso de instalación de Django y obtener un sistema de trabajo junto con nuestros otros componentes. La red presenta muchas opciones para la implementación. Aquí se presenta una versión, pero los usuarios pueden usar sus propios procesos.
Las versiones de PostgreSQL y Nginx disponibles en RHEL 8 se instalarán de manera predeterminada utilizando Yum.
sudo yum install nginx postgresql-server
PostgreSQL requerirá psycopg2, pero debe estar disponible solo en el entorno virtualenv, por lo que lo instalaremos utilizando pip3 junto con Django y Gunicorn. Pero primero necesitamos configurar virtualenv.
Siempre hay mucho debate sobre la elección del lugar correcto para instalar proyectos de Django, pero cuando tenga dudas, siempre puede recurrir al Estándar de jerarquía del sistema de archivos de Linux. En particular, el FHS dice que / srv se usa para: "almacenar datos específicos del host: datos que el sistema proporciona, por ejemplo, datos y scripts de servidores web, datos almacenados en servidores FTP, así como repositorios de sistemas de control versiones (introducidas en FHS-2.3 en 2004) ".
Este es solo nuestro caso, por lo que ponemos todo lo que necesitamos en / srv, propiedad de nuestro usuario de la aplicación (usuario de la 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 y Django es sencillo: crear una base de datos, crear un usuario, configurar permisos. Hay un punto a tener en cuenta al configurar PostgreSQL por primera vez: el script postgresql-setup, que se instala con el paquete postgresql-server. Este script lo ayuda a realizar tareas básicas relacionadas con la administración de un clúster de base de datos, como inicializar un clúster o actualizar el proceso. Para configurar una nueva instancia de PostgreSQL en el sistema RHEL, necesitamos ejecutar el comando:
sudo /usr/bin/postgresql-setup -initdb
Después de eso, puede iniciar PostgreSQL usando systemd, crear una base de datos y configurar el proyecto en Django. Recuerde reiniciar PostgreSQL después de realizar cambios en el archivo de configuración de autenticación del cliente (generalmente pg_hba.conf) para configurar el almacenamiento de contraseña para el usuario de la aplicación. Si encuentra otras dificultades, asegúrese de cambiar la configuración de IPv4 e IPv6 en el archivo 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
En el archivo /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
En el archivo /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 }}', } }
Después de configurar el archivo settings.py en el proyecto y configurar la configuración de la base de datos, puede iniciar el servidor de desarrollo para asegurarse de que todo funcione. Después de iniciar el servidor de desarrollo, es bueno crear un usuario administrador para probar la conexión a la base de datos.
./manage.py runserver 0.0.0.0:8000 ./manage.py createsuperuser
WSGI? Wai
El servidor de desarrollo es útil para las pruebas, pero para ejecutar la aplicación, debe configurar el servidor y el proxy adecuados para la Interfaz de puerta de enlace del servidor web (WSGI). Hay varios paquetes comunes, por ejemplo, Apache HTTPD con uWSGI o Nginx con Gunicorn.
El objetivo de la Interfaz de puerta de enlace del servidor web es redirigir las solicitudes del servidor web al marco web de Python. WSGI es una especie de legado del pasado terrible cuando los mecanismos CGI estaban en uso, y hoy WSGI es en realidad el estándar, independientemente del servidor web o el marco Python utilizado. Pero a pesar de su amplia distribución, todavía hay muchos matices cuando se trabaja con estos marcos y muchas opciones. En este caso, intentaremos establecer una interacción entre Gunicorn y Nginx a través del socket.
Dado que ambos componentes están instalados en el mismo servidor, intentaremos usar un socket UNIX en lugar de un socket de red. Como la comunicación requiere un socket de todos modos, intentemos un paso más y configuremos la activación del socket para Gunicorn a través de systemd.
El proceso de creación de servicios activados por socket es bastante simple. Primero, se crea un archivo de unidad que contiene la directiva ListenStream, que apunta al punto en el que se creará el socket UNIX, luego un archivo de unidad para el servicio, donde la directiva Requiere apuntará al archivo de unidad de socket. Luego, en el archivo unitario del servicio, solo queda llamar a Gunicorn desde el entorno virtual y crear un enlace WSGI para el socket UNIX y la aplicación Django.
Aquí hay algunos ejemplos de archivos de unidad que se pueden tomar como base. Primero, configure el zócalo.
[Unit] Description=Gunicorn WSGI socket [Socket] ListenStream=/run/gunicorn.sock [Install] WantedBy=sockets.target
Ahora necesitas configurar el demonio 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, simplemente cree un archivo de configuración de proxy y configure un directorio para almacenar contenido estático si lo usa. En RHEL, los archivos de configuración de Nginx son /etc/nginx/conf.d. Puede copiar el siguiente ejemplo allí al archivo /etc/nginx/conf.d/default.conf e iniciar el servicio. Asegúrese de especificar server_name según su nombre 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; } }
Ejecute los sockets Gunicorn y Nginx usando systemd, y puede comenzar a probar.
Error de puerta de enlace incorrecta?
Si ingresa la dirección en el navegador, lo más probable es que obtenga el error 502 Bad Gateway. Puede ser causado por permisos configurados incorrectamente para el socket UNIX, o debido a problemas más complejos relacionados con el control de acceso en SELinux.
En el registro de errores de nginx, puede ver una línea 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"
Si probamos Gunicorn directamente, obtenemos una respuesta vacía.
curl —unix-socket /run/gunicorn.sock 8beta1.example.com
Veamos por qué está sucediendo esto. Si abre el registro, lo más probable es que veamos que el problema está relacionado con SELinux. Como estamos ejecutando un demonio para el cual no hemos creado nuestra propia política, está marcado como init_t. Probemos esta teoría en la práctica.
sudo setenforce 0
Todo esto puede causar críticas y lágrimas sangrientas, pero esto solo está depurando el prototipo. Desactivamos el cheque solo para asegurarnos de que este sea el problema, después de lo cual devolveremos todo a sus lugares.
Al actualizar la página en el navegador o al reiniciar nuestro comando curl, puede ver la página de prueba de Django.
Entonces, asegurándonos de que todo funcione y que no haya más problemas de permisos, volvemos a habilitar SELinux.
sudo setenforce 1
No se hablará sobre audit2allow y la creación de políticas basadas en alertas utilizando sepolgen, ya que actualmente no hay una aplicación real de Django, no hay un mapa completo de lo que Gunicorn podría querer acceder, y qué acceso se debe denegar. Por lo tanto, es necesario mantener SELinux funcionando para proteger el sistema y, al mismo tiempo, permitir que la aplicación se inicie y deje mensajes en el registro de auditoría para que luego pueda crear una política real basada en ellos.
Especificar dominios permitidos
No todos han oído hablar de dominios permitidos en SELinux, pero no hay nada nuevo en ellos. Muchos incluso trabajaron con ellos, sin darse cuenta ellos mismos. Cuando se crea una política basada en mensajes de auditoría, la política creada es un dominio permitido. Tratemos de crear la política permisiva más simple.
Para crear un dominio permitido específico para Gunicorn, necesita una determinada política y también deberá marcar los archivos apropiados. Además, se necesitan herramientas para ensamblar nuevas políticas.
sudo yum install selinux-policy-devel
El mecanismo de dominio resuelto es una gran herramienta para identificar problemas, especialmente cuando se trata de una aplicación personalizada o aplicaciones que vienen sin políticas ya creadas. En este caso, la política de dominio permitida para Gunicorn será lo más simple posible: declare el tipo principal (gunicorn_t), declare el tipo que usaremos para marcar varios archivos ejecutables (gunicorn_exec_t) y luego configure la transición para que el sistema marque correctamente los procesos en ejecución . La última línea establece la política como habilitada por defecto en el momento de su 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;
Puede compilar este archivo de política y agregarlo al sistema.
make -f /usr/share/selinux/devel/Makefile sudo semodule -i gunicorn.pp sudo semanage permissive -a gunicorn_t sudo semodule -l | grep permissive
Verifiquemos si SELinux está bloqueando cualquier otra cosa además de lo que nuestro demonio desconocido está accediendo.
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 evita que Nginx escriba datos en el socket UNIX utilizado por Gunicorn. Por lo general, en tales casos, los políticos comienzan a cambiar, pero hay otras tareas por delante. También puede cambiar la configuración del dominio convirtiéndolo de un dominio de restricción en un dominio de permiso. Ahora mueva httpd_t al dominio de permiso. Esto proporcionará a Nginx el acceso necesario y podremos continuar trabajando en la depuración.
sudo semanage permissive -a httpd_t
Entonces, cuando fue posible preservar la protección de SELinux (de hecho, no debe dejar el proyecto con SELinux en modo de restricción) y los dominios de permisos están cargados, debe averiguar qué debe marcarse exactamente como gunicorn_exec_t para que todo vuelva a funcionar como se esperaba. Intentemos acceder al sitio web para ver nuevos mensajes sobre restricciones de acceso.
sudo ausearch -m AVC -c gunicorn
Puede ver muchos mensajes que contienen 'comm = “gunicorn”' que realizan varias acciones en archivos en / srv / djangoapp, por lo que obviamente este es solo uno de los comandos que debe marcar.
Pero además, aparece un mensaje como este:
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
Si observa el estado del servicio gunicorn o ejecuta el comando ps, no aparecerá ningún proceso en ejecución. Gunicorn parece estar intentando acceder al intérprete de Python en nuestro entorno virtualenv, posiblemente para ejecutar scripts de trabajo (trabajadores). Así que ahora marquemos estos dos ejecutables y veamos si podemos abrir nuestra página de prueba de Django.
chcon -t gunicorn_exec_t /srv/djangoapp/django/bin/gunicorn /srv/djangoapp/django/bin/python3.6
Deberá reiniciar el servicio gunicorn para poder seleccionar una nueva etiqueta. Puede reiniciarlo de inmediato o detener el servicio y dejar que el socket lo inicie cuando abra el sitio en un navegador. Asegúrese de que los procesos obtengan las etiquetas correctas utilizando ps.
ps -efZ | grep gunicorn
¡Recuerde crear una política SELinux normal más tarde!
Si mira los mensajes AVC ahora, el último mensaje contiene permissive = 1 para todo lo relacionado con la aplicación, y permissive = 0 para el resto del sistema. Si comprende qué tipo de acceso necesita una aplicación real, puede encontrar rápidamente la mejor manera de resolver tales problemas. Pero hasta entonces, es mejor que el sistema esté protegido, y para obtener una auditoría clara y utilizable por parte del proyecto Django.
sudo ausearch -m AVC
Resultó!
Apareció un proyecto de Django en funcionamiento con una interfaz en Nginx y Gunicorn WSGI. Configuramos Python 3 y PostgreSQL 10 desde los repositorios RHEL 8 Beta. Ahora puede continuar y crear (o simplemente implementar) aplicaciones Django o explorar otras herramientas disponibles en RHEL 8 Beta para automatizar el proceso de ajuste, mejorar el rendimiento o incluso contener esta configuración.