angle-uparrow-clockwisearrow-counterclockwisearrow-down-uparrow-leftatcalendarcard-listchatcheckenvelopefolderhouseinfo-circlepencilpeoplepersonperson-fillperson-plusphoneplusquestion-circlesearchtagtrashx

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.

18 octubre 2019 Actualizado 18 octubre 2019
post main image
unsplash.com/@emilydafinchy

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/

Deje un comentario

Comente de forma anónima o inicie sesión para comentar.

Comentarios

Deje una respuesta.

Responda de forma anónima o inicie sesión para responder.