ISPConfig: Ejecutar un Python Flask Docker contenedor como un usuario de Shell encarcelado
El método descrito requiere que usted pueda ser root, lo que significa que no es universal pero puede ser suficiente si usted es el administrador del sistema.
Dirijo un servidor con ISPConfig unos 50 sitios. Los sitios son estáticos o PHPestoy desarrollando Python Flask aplicaciones ahora y también quiero ejecutarlos en el ISPConfig servidor, puedes crear entornos virtuales en el servidor y ISPConfig ejecutar tu aplicación desde aquí, pero hace algún tiempo he optado por utilizarlos para el desarrollo Docker y la producción. staging Se necesita tiempo para establecer esto, pero realmente vale la pena. Docker
Usaré un Usuario de Shell encarcelado para ejecutar el contenedor. La razón es que cuando el contenedor rompe el acceso se limita a los derechos del Usuario de Shell encarcelado, ¿verdad? Véase también el resumen a continuación.
Mi ISPConfig sistema:
- ISPConfig3 3.1.13
- Debian 9 (Estiramiento)
- MariaDB 10.3
- Nginx 1.10.3
Mi Docker contenedor encendido ISPConfig
Para staging la producción del Python Flask Docker contenedor, basado en Alpine y que contiene Pythonla aplicación y el servidor web Gunicorn WSGI, utiliza un mapeo de'volúmenes' para registrar archivos, archivos de sesión y archivos de caché. Además, contiene una asignación de'volúmenes' al directorio estático.
Para el desarrollo y la producción staging, utilizo un archivo.dockerignore para excluir la carpeta estática del archivo Docker image. Para el desarrollo no lo necesitamos de todos modos, ya que servimos todo fuera del Docker contenedor. Para staging la producción tampoco queremos la carpeta estática en el contenedor. Aquí servimos los artículos estáticos no con, Gunicorn sino directamente con Nginx...
El Docker Python Flask contenedor no contiene una base de datos, etc., pero utiliza los ISPConfig servicios para una fácil configuración y gestión:
- La gestión de ISPConfig dominios y sitios (incluyendo Letsencrypt SSL)
- La base de datos del host (MariaDB), conectarse a través de un socket
- El correo del host (Postfix), se conecta a través de port 25
- El servidor web del host (Nginx), invierte proxy y sirve estática
Tenga en cuenta que construyo el Docker image en mi máquina local usando:
docker save ...
El archivo tar resultante se copia en el ISPConfig servidor y se desempaqueta, ver abajo.
Para configurar ISPConfig nuestro sitio hacemos lo habitual:
- Instalar Docker y Dockercomponer (una sola vez)
- Añadir dominio
- Agregar sitio web, establecer SSL con Letsencrypt
- Añadir usuario de base de datos y base de datos
- Añada un usuario de Shell encarcelado (!), Chroot Shell: Jailkit
Directorios de ISPConfigusuarios y grupos
Cuando agregamos el sitio (y creamos el usuario shell) ISPConfig creamos un usuario (Linux) para él:
- Nombre de usuario: peterpepyco
El usuario y grupo de linux, ver ISPConfig -> Shell User -> Options, en mi caso:
- Nombre de usuario web: web73
- Grupo Web: cliente2
También puede ver esto iniciando sesión con el usuario de Shell y recorriendo algunos directorios, haciendo un 'ls -n'.
Hay una diferencia entre un Usuario de Shell encarcelado y un Usuario de Shell no encarcelado. En ambos casos el directorio base es:
/var/www/clients/client2/web73
El directorio raíz es:
/var/www/clients/client2/web73/home/peterpepyco
y el directorio web lo es:
/var/www/clients/client2/web73/web
Cuando el usuario Shell está encarcelado, la raíz del sistema de archivos cambia al directorio base, para obtener el grupo del tipo de usuario Shell:
groups
que regresa en mi caso:
client2
Para ejecutar el Docker como un usuario diferente necesitamos el ID de usuario, UID, y el ID de grupo, GID. Para obtener el tipo de UID:
id -u
que devuelve en mi caso 5055, y:
id -g
que devuelve en mi caso 5006. Hay muchas maneras de obtener UID y GID. También puede escribir a máquina:
cat /etc/passwd
que regresa:
root:x:0:0:root:/root:/bin/bash
peterpepyco:x:5055:5006:::/bin/bash
y
cat /etc/group
que regresa:
root:x:0:
client2:x:5006:
También puede crear un archivo, 'echo """ > a', y luego hacer 'ls -n', etc.
Modificar la Nginx configuración
En ISPConfig vaya al sitio y seleccione la pestaña Opciones. En la sección Nginx Directivas, pegue:
location / {
proxy_pass http://127.0.0.1:8000;
proxy_set_header Host $host;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
client_max_body_size 1M;
}
location /static {
alias /var/www/clients/client2/web73/web/static;
}
Tenga en cuenta que la inversión proxy pasa las peticiones al puerto 8000. Este es el puerto que el servidor Gunicorn WSGI en el contenedor está escuchando.
Copiar los archivos
En la página de inicio de Shell User creo un directorio docker donde copio el contenedor comprimido, el archivo de variables de entorno, los docker-compose archivos y la base de datos. Después de la copia el directorio se ve como:
.
└── docker
├── .env
├── docker-compose_base_1.283_production.yml
├── docker-compose_production.yml
├── docker-volumes
│ ├── cache
│ │ ├── other
│ │ ├── query_result
│ │ └── render_template
│ ├── flask_session
│ └── log
├── peterspython2.dump_20191017
├── peterspython_image_web_1.283.tar
└── project
├── Dockerfile
└── requirements.txt
Para cargar la base de datos:
mysql -upeterspythonuser -p peterspython2 < peterspython2.dump_20191017
Para cargar la imagen de la ventana acoplable, es necesario que sea root:
docker load -i peterspython_image_web_1.283.tar
El siguiente paso es copiar la carpeta estática de mi sistema local a la carpeta'/web', ver también arriba.
Agregar usuario, UID, y grupo, GID, a docker-compose y Dockerarchivo
Utilizo Docker-componer para iniciar y detener el contenedor, volúmenes de mapa, etc. El archivo.env contiene una serie de variables de configuración que pasamos a . Primera parte de este archivo:
# production environment vars
PROJECT_NAME=peterspython
FLASK_CONFIG=production
# docker-compose, docker
# peterpepyco:client2
CONTAINER_USER=peterpepyco
CONTAINER_UID=5055
CONTAINER_GROUP=client2
CONTAINER_GID=5006
...
La primera parte del archivo de composición:
# docker-compose_base.yml
version: '3.2'
services:
web:
image: ${PROJECT_NAME}_image_web:1.283
container_name: ${PROJECT_NAME}_container_web
env_file:
- ./.env
restart: always
build:
context: ./project
dockerfile: Dockerfile
args:
- CONTAINER_USER=${CONTAINER_USER}
- CONTAINER_UID=${CONTAINER_UID}
- CONTAINER_GROUP=${CONTAINER_GROUP}
- CONTAINER_GID=${CONTAINER_GID}
ports:
- "${SERVER_PORT_HOST}:${SERVER_PORT_CONTAINER}"
volumes:
...
y luego en el Dockerarchivo:
...
# create and set working directory
RUN mkdir -p /home/flask/project
WORKDIR /home/flask/project
# copy app code into container
COPY . ./
# create group and user used in this container
ARG CONTAINER_USER
ARG CONTAINER_UID
ARG CONTAINER_GROUP
ARG CONTAINER_GID
RUN addgroup -g $CONTAINER_GID $CONTAINER_GROUP && \
adduser -D -H -G $CONTAINER_GROUP -u $CONTAINER_UID $CONTAINER_USER && \
chown -R $CONTAINER_USER:$CONTAINER_GROUP /home/flask
USER $CONTAINER_USER
Inicio del contenedor
De nuevo esto requiere que seas root:
docker-compose -f docker-compose_base_1.283_production.yml -f docker-compose_production.yml up -d
El resultado es:
Creating network "docker_default" with the default driver
Creating peterspython_container_web ... done
Si el contenedor no comienza a verificar los registros, los mensajes. Si se ejecuta pero recibe errores, puede introducir el contenedor en ejecución, primero obtenga el identificador del contenedor:
docker ps
que regresa:
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
292aa9bcecaf peterspython_image_web:1.283 "/usr/local/bin/guni…" 18 hours ago Up 18 hours 0.0.0.0:8000->8000/tcp peterspython_container_web
A continuación, introduzca el contenedor en marcha:
docker exec -it 292aa9bcecaf sh
Tenga en cuenta que empezamos sh y no bash porque bash no está en la Alpine imagen.
Resumen
No es realmente difícil una vez que entiendes (parte de) Docker y (parte de) ISPConfig. Ahora puedes ejecutar todo lo que quieras en un ISPConfig servidor.
He utilizado un máximo de ISPConfig servicios, estoy contento con el uso, MariaDB pero algunas personas pueden quejarse de que, por ejemplo, PostgreSQL no es compatible. Sería bueno que ISPConfig agregara PostgreSQL como una opción. Eso sería mejor que añadir un servicio de PostgreSQL al contenedor, aumentando el tamaño del contenedor.
Un problema es que necesitamos ser root cuando cargamos Docker image y arrancamos y paramos el contenedor, de hecho esto es necesario para cada Docker comando, lo que significa que este método no es adecuado sólo para algunos clientes aleatorios. Sería bueno que ISPConfig soportara un método para permitir comandos por sitio Docker y Docker-Componer. También los contenedores exponen los puertos que pueden entrar en conflicto con los ya existentes. Esto se puede resolver asignando un intervalo de puertos por centro.
¿Es lo suficientemente ISPConfig segura la configuración con las credenciales de usuario de Shell encarceladas para ejecutar el contenedor? Veo un posible problema con el uso del UID y el inicio del contenedor como root. El UID del Usuario Shell encarcelado peterpepyco es el mismo que el UID de web73, lo que significa que el Docker contenedor funciona de hecho como web73:client2 y no como peterpepyco:client2. Tengo que seguir investigando. Tal vez se pueda utilizar el espacio de nombres. Pero por el momento estoy contento de que esté corriendo.
Enlaces / créditos
How do I add a user when I'm using Alpine as a base image?
https://stackoverflow.com/questions/49955097/how-do-i-add-a-user-when-im-using-alpine-as-a-base-image
Running Docker Containers as Current Host User
https://jtreminio.com/blog/running-docker-containers-as-current-host-user/
Compose file version 3 reference
https://docs.docker.com/compose/compose-file/
How to copy Docker images from one host to another without using a repository
https://stackoverflow.com/questions/23935141/how-to-copy-docker-images-from-one-host-to-another-without-using-a-repository
Isolate containers with a user namespace
https://docs.docker.com/engine/security/userns-remap/
Leer más
Docker Docker-compose Flask ISPConfig
Recientes
- Cómo ocultar las claves primarias de la base de datos UUID de su aplicación web
- Don't Repeat Yourself (DRY) con Jinja2
- SQLAlchemy, PostgreSQL, número máximo de filas por user
- Mostrar los valores en filtros dinámicos SQLAlchemy
- Transferencia de datos segura con cifrado de Public Key y pyNaCl
- rqlite: una alternativa de alta disponibilidad y dist distribuida SQLite
Más vistos
- Usando Python's pyOpenSSL para verificar los certificados SSL descargados de un host
- Usando UUIDs en lugar de Integer Autoincrement Primary Keys con SQLAlchemy y MariaDb
- Usando PyInstaller y Cython para crear un ejecutable de Python
- Conectarse a un servicio en un host Docker desde un contenedor Docker
- SQLAlchemy: Uso de Cascade Deletes para eliminar objetos relacionados
- Flask RESTful API validación de parámetros de solicitud con esquemas Marshmallow