RHEL 8 Beta ofrece a los desarrolladores muchas características nuevas, cuya lista podría ocupar páginas, pero aprender cosas nuevas siempre es mejor mediante la práctica, por lo que a continuación ofrecemos una guía práctica para crear una infraestructura de aplicaciones real basada en Red Hat Enterprise. Linux 8 Beta.

Tomemos como base Python, un lenguaje de programación popular entre los desarrolladores, una combinación de Django y PostgreSQL, una combinación bastante común para crear aplicaciones, y configuremos RHEL 8 Beta para trabajar con ellos. Luego agregaremos un par de ingredientes más (sin clasificar).
El entorno de prueba cambiará, porque es interesante explorar las posibilidades de la automatización, trabajar con contenedores y probar entornos con múltiples servidores. Para comenzar con un nuevo proyecto, puede comenzar creando un prototipo pequeño y simple a mano para que pueda ver exactamente qué debe suceder y cómo interactúa, y luego continuar para automatizar y crear configuraciones más complejas. Hoy estamos hablando de la creación de tal prototipo.
Comencemos implementando la imagen de VM Beta de RHEL 8. Puede instalar una máquina virtual desde cero o utilizar la imagen de invitado KVM disponible con su suscripción Beta. Cuando utilice una imagen invitada, deberá configurar un CD virtual que contendrá metadatos y datos de usuario para la inicialización en la nube (cloud-init). No necesita hacer nada especial con la estructura del disco o los paquetes disponibles; cualquier configuración servirá.
Echemos un vistazo más de cerca a todo el proceso.
Instalando Django
Con la versión más reciente de Django, necesitarás un entorno virtual (virtualenv) con Python 3.5 o posterior. En las notas Beta puedes ver que Python 3.6 está disponible, comprobemos si efectivamente es así:
[cloud-user@8beta1 ~]$ python
-bash: python: command not found
[cloud-user@8beta1 ~]$ python3
-bash: python3: command not found
Red Hat utiliza activamente Python como conjunto de herramientas del sistema en RHEL, entonces, ¿por qué ocurre esto?
El hecho es que muchos desarrolladores de Python todavía están contemplando la transición de Python 2 a Python 2, mientras que Python 3 está en desarrollo activo y constantemente aparecen más y más versiones nuevas. Por lo tanto, para satisfacer la necesidad de herramientas de sistema estables y al mismo tiempo ofrecer a los usuarios acceso a varias versiones nuevas de Python, el sistema Python se trasladó 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 realizaron en la publicación en (Langdon Blanco).
Entonces, para que Python funcione, solo necesita instalar dos paquetes, con python3-pip incluido como dependencia.
sudo yum install python36 python3-virtualenv
¿Por qué no utilizar llamadas directas al módulo como sugiere Langdon e instalar pip3? Teniendo en cuenta la próxima automatización, se sabe que Ansible requerirá pip instalado para ejecutarse, ya que el módulo pip no admite virtualenvs con un ejecutable de pip personalizado.
Con un intérprete de Python3 funcional a su disposición, puede continuar con el proceso de instalación de Django y tener un sistema que funcione junto con nuestros otros componentes. Hay muchas opciones de implementación disponibles en Internet. Aquí se presenta una versión, pero los usuarios pueden usar sus propios procesos.
Instalaremos las versiones de PostgreSQL y Nginx disponibles en RHEL 8 de forma predeterminada usando Yum.
sudo yum install nginx postgresql-server
PostgreSQL requerirá psycopg2, pero debe estar disponible sólo en un entorno virtualenv, por lo que lo instalaremos usando pip3 junto con Django y Gunicorn. Pero primero necesitamos configurar virtualenv.
Siempre hay mucho debate sobre el lugar correcto para instalar proyectos Django, pero en caso de duda, siempre puedes consultar el estándar. Linux Estándar de jerarquía del sistema de archivos (FHS). Específicamente, el FHS establece que /srv se utiliza para: "almacenar datos específicos del host: datos producidos por el sistema, como datos y scripts del servidor web, datos almacenados en servidores FTP y repositorios del sistema de control de versiones (que aparecieron en FHS-2.3 en 2004)".
Este es exactamente nuestro caso, por lo que ponemos todo lo que necesitamos en /srv, que es 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 fácil: cree una base de datos, cree un usuario, configure permisos. Una cosa a tener en cuenta al instalar PostgreSQL inicialmente es el script de configuración de postgresql que se instala con el paquete postgresql-server. Este script le ayuda a realizar tareas básicas asociadas con la administración del clúster de bases de datos, como la inicialización del clúster o el proceso de actualización. Para configurar una nueva instancia de PostgreSQL en un sistema RHEL, necesitamos ejecutar el comando:
sudo /usr/bin/postgresql-setup -initdb
Luego puede iniciar PostgreSQL usando systemd, crear una base de datos y configurar un 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ñas 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 base de datos, puede iniciar el servidor de desarrollo para asegurarse de que todo funcione. Después de iniciar el servidor de desarrollo, es una buena idea 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? ¿Espere?
El servidor de desarrollo es útil para realizar 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). Existen varias combinaciones comunes, por ejemplo, Apache HTTPD con uWSGI o Nginx con Gunicorn.
El propósito de la interfaz de puerta de enlace del servidor web es reenviar solicitudes desde Servidor web El framework web Python. WSGI es una reliquia del terrible pasado, cuando se utilizaban mecanismos CGI, y hoy en día es un estándar de facto, independientemente del servidor web o framework Python utilizado. Sin embargo, a pesar de su amplia adopción, aún existen muchos matices al trabajar con estos frameworks y una amplia gama de opciones. En este caso, intentaremos establecer comunicación entre Gunicorn y Nginx mediante un socket.
Dado que ambos componentes están instalados en el mismo servidor, intentemos usar un socket UNIX en lugar de un socket de red. Dado que la comunicación requiere un socket en cualquier caso, intentemos dar un paso más y configurar la activación del socket para Gunicorn a través de systemd.
El proceso de creación de servicios activados por socket es bastante sencillo. Primero, se crea un archivo de unidad que contiene una directiva ListenStream que apunta al punto en el que se creará el socket UNIX, luego un archivo de unidad para el servicio en el que la directiva Requires apuntará al archivo de unidad del socket. Luego, en el archivo de la unidad de servicio, todo lo que queda es llamar a Gunicorn desde el entorno virtual y crear un enlace WSGI para el socket UNIX y la aplicación Django.
A continuación se muestran algunos ejemplos de archivos unitarios que puede utilizar como base. Primero configuramos el enchufe.
[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, es una simple cuestión de crear archivos de configuración de proxy y configurar un directorio para almacenar contenido estático si está usando uno. En RHEL, los archivos de configuración de Nginx se encuentran en /etc/nginx/conf.d. Puede copiar el siguiente ejemplo en el archivo /etc/nginx/conf.d/default.conf e iniciar el servicio. Asegúrese de configurar server_name para que coincida con 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;
}
}
Inicie el socket Gunicorn y Nginx usando systemd y estará listo para comenzar a realizar pruebas.
¿Error de puerta de enlace incorrecta?
Si introduces la dirección en un navegador, probablemente obtendrás un error 502 Bad Gateway. Esto podría deberse a permisos configurados incorrectamente en el socket UNIX, o a problemas más complejos relacionados con el control de acceso en SE.Linux.
En el registro de errores de nginx puedes 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, obtendremos una respuesta vacía.
curl —unix-socket /run/gunicorn.sock 8beta1.example.com
Vamos a averiguar por qué está sucediendo esto. Si abres el registro, lo más probable es que veas que el problema está relacionado con SE.LinuxDado que estamos ejecutando un demonio que no tiene su propia política, se marca como init_t. Probemos esta teoría en la práctica.
sudo setenforce 0
Todo esto puede provocar críticas y lágrimas de sangre, pero esto es sólo una depuración del prototipo. Desactivemos la verificación solo para asegurarnos de que este sea el problema, después de lo cual devolveremos todo a su lugar.
Al actualizar la página en el navegador o volver a ejecutar nuestro comando curl, podrá ver la página de prueba de Django.
Así que, después de asegurarnos de que todo funciona y no hay más problemas con los permisos, volvemos a encender SE.Linux.
sudo setenforce 1
Este artículo no tratará sobre audit2allow ni sobre la creación de políticas basadas en alertas con sepolgen, ya que actualmente no existe una aplicación Django real, por lo que no hay un mapa completo de a qué podría acceder Gunicorn y qué debería denegarse. Por lo tanto, es importante mantener SE en funcionamiento.Linux para proteger el sistema, permitiendo al mismo tiempo que la aplicación se ejecute y deje mensajes en el registro de auditoría para que posteriormente se pueda crear una política real basada en ellos.
Especificación de dominios permisivos
Acerca de los dominios permitidos en SELinux No todo el mundo las conoce, pero no son nada nuevo. Muchos incluso han trabajado con ellas sin darse cuenta. Cuando se crea una política basada en mensajes de auditoría, la política resultante representa un dominio permitido. Intentemos crear una política de permisos sencilla.
Para crear un dominio permitido específico para Gunicorn, necesita algún tipo de política y también debe marcar los archivos apropiados. Además, se necesitan herramientas para elaborar nuevas políticas.
sudo yum install selinux-policy-devel
El mecanismo de dominios permitidos es una gran herramienta para identificar problemas, especialmente cuando se trata de una aplicación personalizada o aplicaciones que se envían sin políticas ya creadas. En este caso, la política de dominio permitido para Gunicorn será lo más simple posible: declarar un tipo principal (gunicorn_t), declarar un tipo que usaremos para marcar múltiples ejecutables (gunicorn_exec_t) y luego configurar una transición para que el sistema marque correctamente procesos en ejecución. La última línea establece la política como habilitada de forma predeterminada en el 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;
Puede compilar este archivo de política y agregarlo a su 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 a comprobar si SE está bloqueandoLinux algo más aparte de lo que nuestro demonio desconocido está abordando.
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 Esto impide que Nginx escriba datos en el socket UNIX utilizado por Gunicorn. Normalmente, en estos casos se modifican las políticas, pero aún quedan otros problemas por resolver. También se puede cambiar la configuración del dominio, transformándolo de un dominio restringido a uno permisivo. Ahora, movamos httpd_t al dominio permisivo. Esto proporcionará a Nginx el acceso necesario y podremos continuar con la depuración.
sudo semanage permissive -a httpd_t
Entonces, ¿cuándo lograste guardar la protección SE?Linux (de hecho, no deberías dejar el proyecto con SELinux Si el sistema está en modo restringido y se están cargando los dominios de permisos, debemos averiguar qué es exactamente lo que debe marcarse como gunicorn_exec_t para que todo vuelva a funcionar correctamente. Intentemos acceder al sitio web para ver si hay nuevos mensajes sobre restricciones de acceso.
sudo ausearch -m AVC -c gunicorn
Verás muchos mensajes que contienen 'comm="gunicorn"' que hacen varias cosas en archivos en /srv/djangoapp, por lo que este es obviamente uno de los comandos que vale la pena 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 verá ningún proceso en ejecución. Parece que gunicorn está intentando acceder al intérprete de Python en nuestro entorno virtualenv, posiblemente para ejecutar scripts de trabajo. Ahora marquemos estos dos archivos ejecutables y verifiquemos 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
Será necesario reiniciar el servicio gunicorn antes de poder seleccionar la nueva etiqueta. Puede reiniciarlo inmediatamente o detener el servicio y dejar que el socket lo inicie cuando abra el sitio en el navegador. Verifique que los procesos hayan recibido las etiquetas correctas usando ps.
ps -efZ | grep gunicorn
No olvides crear una política SE normal más adelante.Linux!
Si observa 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, podrá encontrar rápidamente la mejor manera de resolver dichos problemas. Pero hasta entonces, es mejor mantener el sistema seguro y obtener una auditoría clara y utilizable del proyecto Django.
sudo ausearch -m AVC
¡Resultó!
Ha aparecido un proyecto Django en funcionamiento con una interfaz basada en Nginx y Gunicorn WSGI. Configuramos Python 3 y PostgreSQL 10 desde los repositorios RHEL 8 Beta. Ahora puede avanzar y crear (o simplemente implementar) aplicaciones Django o explorar otras herramientas disponibles en RHEL 8 Beta para automatizar el proceso de configuración, mejorar el rendimiento o incluso contener esta configuración.
Fuente: habr.com
