angle-up arrow-clockwise arrow-counterclockwise arrow-down-up arrow-left at calendar card-list chat check envelope folder house info-circle pencil people person person-plus phone plus question-circle search tag trash x

ISPConfig: L'utilisation d'un Python Flask Docker conteneur en tant qu'utilisateur Shell emprisonné.

18 octobre 2019 Mise à jour 18 octobre 2019 à côté de Peter

La méthode décrite nécessite que vous puissiez être root, ce qui signifie qu'elle n'est pas universelle mais peut être suffisante si vous êtes l'administrateur système.

post main image
unsplash.com/@emilydafinchy

Je gère un serveur avec ISPConfig une cinquantaine de sites. Les sites sont statiques ou PHP... Je suis en train de développer Python Flask des applications maintenant et je veux aussi les exécuter sur le ISPConfig serveur... Vous pouvez créer des environnements virtuels sur le serveur et ISPConfig exécuter votre application d'ici... Mais il ya quelque temps j'ai choisi d'utiliser pour le développement Docker , et la production. staging Il faut du temps pour le mettre en place, mais ça en vaut vraiment la peine. Docker C'est une bonne chose.

J'utiliserai un utilisateur Shell emprisonné pour faire fonctionner le conteneur. La raison en est que lorsque le conteneur casse l'accès est limité aux droits de l'utilisateur Shell emprisonné, n'est-ce pas ? Voir aussi le résumé ci-dessous.

Mon ISPConfig système :

  • ISPConfig3 3.1.13
  • Debian 9 (Étirer)
  • MariaDB 10.3
  • Nginx 1.10.3

Mon Docker conteneur sur ISPConfig

Pour staging la production du Python Flask Docker conteneur, basé sur Alpine et contenant Pythonl'application et le serveur web Gunicorn WSGI, utilise un mappage'volumes' pour les fichiers journaux, les fichiers de session et les fichiers cache. De plus, il contient un mappage'volumes' vers le répertoire statique.

Pour le développement, stagingla production et la production, j'utilise un fichier.dockerignore pour exclure le dossier statique de l'image Docker image. Pour le développement, nous n'en avons de toute façon pas besoin car nous servons tout en dehors du Docker conteneur. Pour staging et la production, nous ne voulons pas non plus le dossier statique dans le conteneur. Ici nous servons les articles statiques non pas avec Gunicorn mais directement avec Nginx.

Le Docker Python Flask conteneur ne contient pas de base de données, etc. mais utilise les ISPConfig services pour faciliter la configuration et la gestion :

  • La gestion du ISPConfig domaine et des sites (y compris Letsencrypt SSL)
  • La base de données hôte (MariaDB), se connecte via une prise femelle
  • Le courrier hôte (Postfix), connectez-vous via port 25
  • Le serveur web hôte (Nginx), inverser proxy et servir statique

Notez que je construis le fichier Docker image sur ma machine locale en utilisant :

docker save ...

Le fichier tar résultant est copié sur le ISPConfig serveur et décompressé, voir ci-dessous.

Pour configurer ISPConfig notre site, nous faisons comme d'habitude :

  • Installer Docker et Docker-composer (une seule fois)
  • Ajouter un domaine
  • Ajouter un site web, définir Letsencrypt SSL
  • Ajouter un utilisateur de base de données et une base de données
  • Ajouter un utilisateur Shell emprisonné ( !), Chroot Shell : Trousse de prison

Répertoires sur ISPConfig, utilisateur et groupe

Lorsque nous avons ajouté le site (et créé l'utilisateur shell), nous avons ISPConfig créé un (Linux) utilisateur pour ce site, My Shell User :

  • Nom d'utilisateur : peterpepyco

L'utilisateur et le groupe linux, voir ISPConfig -> Utilisateur Shell -> Options, dans mon cas :

  • Nom d'utilisateur Web : web73
  • Groupe Web  : client2

Vous pouvez également le voir en vous connectant avec l'utilisateur Shell et en parcourant certains répertoires, en faisant un'ls -n'.

Il y a une différence entre un utilisateur Shell emprisonné et un utilisateur Shell non emprisonné. Dans les deux cas, le répertoire de base est :

/var/www/clients/client2/web73

Le répertoire d'origine est :

/var/www/clients/client2/web73/home/peterpepyco

et le répertoire web est :

/var/www/clients/client2/web73/web

Lorsque l'utilisateur Shell est emprisonné, la racine du système de fichiers change dans le répertoire de base pour obtenir le groupe du type Utilisateur Shell :

groups

ce qui revient dans mon cas :

client2

Pour exécuter l'application en tant Docker qu'utilisateur différent, nous avons besoin de l'ID utilisateur, UID, et de l'ID de groupe, GID. Pour obtenir le type UID :

id -u

qui revient dans mon cas 5055, et :

id -g

qui revient dans mon cas 5006. Il existe de nombreuses façons d'obtenir l'UID et le GID. Vous pouvez également taper :

cat /etc/passwd

qui revient :

root:x:0:0:root:/root:/bin/bash
peterpepyco:x:5055:5006:::/bin/bash

et

cat /etc/group

qui revient :

root:x:0:
client2:x:5006:

Vous pouvez aussi créer un fichier,'echo "" > a', puis faire'ls -n', etc.

Modifier la Nginx configuration

ISPConfig Allez sur le site et sélectionnez l'onglet Options. Dans la section Nginx Directives, collez :

 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;
  }

Notez que l'inverse proxy passe les requêtes au port 8000. C'est le port sur lequel le serveur Gunicorn WSGI dans le conteneur écoute.

Copie des fichiers

Dans la page d'accueil de l'utilisateur Shell, je crée un docker de répertoire où je copie le conteneur compressé, le fichier des variables d'environnement, les docker-compose fichiers et la base de données. Après la copie, le répertoire ressemble à :

.
└── 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

Pour charger la base de données :

mysql -upeterspythonuser -p peterspython2 < peterspython2.dump_20191017

Pour charger l'image du docker, vous devez être root :

docker load -i peterspython_image_web_1.283.tar

L'étape suivante consiste à copier le dossier statique de mon système local dans le dossier ISPConfig '/web', voir aussi ci-dessus.

Ajout d'un utilisateur, d'un UID, d'un groupe, d'un GID docker-compose et d'un Dockerfichier.

J'utilise Docker-compose pour démarrer et arrêter le conteneur, les volumes de la carte, etc. Le fichier.env contient un certain nombre de variables de configuration que nous passons à . Première partie de ce dossier :

# 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 première partie du fichier de composition :

# 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:
...

et ensuite dans le Dockerdossier :

...
# 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

Démarrage du conteneur

Encore une fois, cela nécessite que vous soyez root :

docker-compose -f docker-compose_base_1.283_production.yml -f docker-compose_production.yml up -d

Le résultat est :

Creating network "docker_default" with the default driver
Creating peterspython_container_web ... done

Si le conteneur ne démarre pas, vérifiez les journaux, les messages. S'il s'exécute mais que vous obtenez des erreurs, vous pouvez entrer le conteneur en cours d'exécution, obtenez d'abord l'ID du conteneur :

docker ps

qui revient :

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

Entrez ensuite dans le conteneur en cours d'exécution :

docker exec -it 292aa9bcecaf sh

Notez que nous commençons sh et non bash parce que bash n'est pas dans l' Alpine image.

Résumé

Ce n'est pas vraiment difficile une fois que vous comprenez (une partie de) Docker et (une partie de) ISPConfig. Vous pouvez maintenant exécuter tout ce que vous voulez sur un ISPConfig serveur.

J'ai utilisé un maximum de ISPConfig services, je suis content d'utiliser MariaDB mais certaines personnes peuvent se plaindre que par exemple PostgreSQL n'est pas supporté. Ce serait bien d'ajouter PostgreSQL en option. Ce serait mieux que d'ajouter un service PostgreSQL au conteneur, ce qui augmenterait la taille du conteneur.

Un problème est que nous avons besoin d'être root lors du chargement Docker image , du démarrage et de l'arrêt du conteneur, en fait c'est nécessaire pour chaque Docker commande, ce qui signifie que cette méthode n'est pas adaptée à seulement quelques clients aléatoires. Ce serait bien si ISPConfig vous pouviez supporter une méthode permettant d'autoriser les commandes par site Docker et Docker-Composer. Les conteneurs exposent également les ports qui peuvent entrer en conflit avec les ports existants. Ceci peut être résolu en assignant une plage de ports par site.

L'installation est-elle suffisamment ISPConfig sécurisée en utilisant les informations d'identification de l'utilisateur Shell emprisonné pour exécuter le conteneur ? Je vois un problème possible avec l'utilisation de l'UID et le démarrage du conteneur root. L'UID de l'utilisateur Shell emprisonné peterpepyco est le même que l'UID de web73, ce qui signifie que le Docker conteneur fonctionne en fait comme web73:client2 et non comme peterpepyco:client2. Je dois approfondir cette question. Peut-être que l'espace de nommage peut être utilisé. Mais pour l'instant, je suis heureux qu'il fonctionne.

Liens / crédits

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/

Laissez un commentaire

Commentez anonymement ou connectez-vous pour commenter.

Commentaires

Laissez une réponse

Répondez de manière anonyme ou connectez-vous pour répondre.