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

ISPConfighet runnen van een Python Flask Docker container als gevangen Shell-gebruiker

De beschreven methode vereist dat u root kunt zijn, wat betekent dat het niet universeel is, maar het kan voldoende zijn als u de systeembeheerder bent.

18 oktober 2019
post main image
unsplash.com/@emilydafinchy

Ik run een server met ISPConfig met ongeveer 50 sites. De sites zijn statisch of PHP. Ik ontwikkel nu Python Flask applicaties en wil ze ook op de ISPConfig server draaien.U kunt virtuele omgevingen creëren op de ISPConfig server en uw applicatie vanaf hier draaien. Maar enige tijd geleden heb ik ervoor gekozen om Docker te gebruiken voor ontwikkeling, staging en productie. Het kost tijd om dit op te zetten, maar het is het echt de moeite waard. Docker is ver weg.

Ik zal een opgesloten Shell User gebruiken om de container te laten draaien. De reden is dat wanneer de container breekt de toegang is beperkt tot de gevangengezette Shell User rechten, is het niet? Zie ook de samenvatting hieronder.

Mijn ISPConfig systeem:

  • ISPConfig3 3.1.13
  • Debian 9 (Rekken)
  • MariaDB 10,3
  • Nginx 1.10.3

Mijn Docker container op ISPConfig.

Voor staging en de productie van de Python Flask container, gebaseerd op Alpine en met Python, de applicatie en de Gunicorn WSGI webserver, maakt gebruik van een 'volumes' mapping naar logbestanden, sessiebestanden en cachebestanden. Daarnaast bevat het een 'volumes' mapping naar de statische directory.

Voor ontwikkeling, staging, en productie gebruik ik een .dockerignore-bestand om de statische map uit te sluiten van de Docker image. Het groeit snel met alle beelden. Voor ontwikkeling hebben we het toch niet nodig omdat we alles buiten de Docker container serveren. Voor staging en productie willen we ook niet de statische map in de container. Hier serveren we de statische items niet met Gunicorn maar direct met Nginx.

De Docker Python Flask container bevat geen database, etc. maar gebruikt de ISPConfig diensten voor eenvoudige configuratie en beheer:

  • Het ISPConfig domein- en locatiebeheer (inclusief Letsencrypt SSL).
  • De host database (MariaDB), aansluiten via een socket.
  • De host-mail (Postfix), verbinden via port 25.
  • De host webserver (Nginx), reverse proxy en serveren statische elektriciteit.

Merk op dat ik de Docker image op mijn lokale machine bouw met behulp van:

docker save ...

Het resulterende tar bestand wordt gekopieerd naar de ISPConfig server en uitgepakt, zie hieronder.

Om ISPConfig voor onze site te configureren doen we het gebruikelijke:

  • Installeer Docker en Docker-compositie (eenmalig).
  • Domein toevoegen
  • Website toevoegen, Letsencrypt SSL toevoegen, Letsencrypt SSL instellen
  • Voeg database gebruiker en database toe
  • Voeg een gevangen (!) Shell User, Chroot Shell toe: Gevangeniskit

Directories op ISPConfig, gebruiker en groep

Toen we de site toevoegden (en de shell user creëerden) maakte ISPConfig een (Linux) gebruiker voor de site. Mijn Shell User:

  • Gebruikersnaam: peterpepyco

De linux gebruiker en groep, zie ISPConfig -> Shell User -> Opties, in mijn geval:

  • Webgebruikersnaam: web73
  • Webgroep: klant2

Je kunt dit ook zien door in te loggen met de Shell User en door een aantal mappen te lopen en een 'ls -n' te doen.

Er is een verschil tussen een opgesloten Shell User en een niet-gejaagde Shell User. In beide gevallen is de basisdirectory dat wel:

/var/www/clients/client2/web73

De thuismap wel:

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

en de web directory is dat ook:

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

Wanneer de Shell User in de gevangenis zit, verandert de root van het bestandssysteem in de basisdirectory.om de groep van het type Shell User te krijgen:

groups

wat in mijn geval terugkomt:

client2

Om de Docker als een andere gebruiker te draaien hebben we de user id, UID, en groep id, GID, nodig. Om het UID-type te krijgen:

id -u

die in mijn geval 5055 terugkeert, en...:

id -g

die terugkeert in mijn geval 5006. Er zijn vele manieren om UID en GID te krijgen. U kunt ook typen:

cat /etc/passwd

die terugkeert:

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

en

cat /etc/group

die terugkeert:

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

Je kunt ook een bestand maken, 'echo "" > a', en dan 'ls -n', etc. doen.

Wijziging van de Nginx -configuratie

In ISPConfig ga naar de site en selecteer de tab Opties. In de Nginx Richtlijnen sectie plakken:

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

Merk op dat omgekeerd proxy verzoeken naar poort 8000 doorgeeft. Dit is de poort waar de Gunicorn WSGI server in de container op luistert.

Kopiëren van de bestanden

In het Shell User home maak ik een directory docker aan waar ik de gecomprimeerde container, het omgevingsvariabelenbestand, de docker-compose bestanden en de database kopieer. Na de kopie ziet de directory er zo uit:

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

Om de database te laden:

mysql -upeterspythonuser -p  peterspython2 <  peterspython2.dump_20191017

Om de dockerafbeelding te laden, moet u de root zijn:

docker load -i  peterspython_image_web_1.283.tar

De volgende stap is het kopiëren van de statische map van mijn lokale systeem naar de ISPConfig '/web' map, zie ook hierboven.

Gebruiker, UID en groep, GID, toevoegen aan docker-compose en Docker-bestand.

Ik gebruik Docker om de container, kaartvolumes, enz. te starten en te stoppen. Het .env bestand bevat een aantal configuratievariabelen die we doorgeven aan docker-compose. Eerste deel van dit bestand:

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

Het eerste deel van het samengestelde bestand:

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

en dan in het Docker-bestand:

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

De container starten

Ook dit vereist dat je wortel bent:

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

Het resultaat is:

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

Als de container niet start, controleer dan de logs, berichten. Als het loopt, maar je krijgt fouten die je kunt invoeren van de lopende container, eerst krijgen de container-id:

docker ps

die terugkeert:

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

Ga dan de lopende container in:

docker exec -it 292aa9bcecaf sh

Merk op dat we sh starten en niet bash omdat bash niet in de Alpine afbeelding voorkomt.

Samenvatting

Het is niet echt moeilijk als je (een deel van) Docker en (een deel van) ISPConfig begrijpt. Nu kun je alles draaien wat je wilt op een ISPConfig server.

Ik heb een maximum aan ISPConfig diensten gebruikt, ik ben blij met het gebruik van MariaDB maar sommige mensen kunnen klagen dat bijvoorbeeld PostgreSQL niet wordt ondersteund. Het zou mooi zijn als ISPConfig PostgreSQL als optie zou toevoegen. Dat zou beter zijn dan het toevoegen van een PostgreSQL service aan de container, waardoor de grootte van de container toeneemt.

Een probleem is dat we root moeten zijn bij het laden van de Docker image en het starten en stoppen van de container, in feite is dit vereist voor elk Docker commando. Dit betekent dat deze methode niet geschikt is voor slechts enkele willekeurige clients. Het zou mooi zijn als ISPConfig een methode zou ondersteunen om per site Docker en Docker-Compose commando's toe te staan. Ook containers stellen havens bloot die in conflict kunnen komen met bestaande havens. Dit kan worden opgelost door een poortbereik per site toe te wijzen.

Is de setup op ISPConfig veilig genoeg om de Shell User referenties te gebruiken om de container te draaien? Ik zie een mogelijk probleem met het gebruik van de UID en het starten van de container als root. De UID van de opgesloten Shell User peterpepyco is hetzelfde als de UID van web73, wat betekent dat de Docker container in feite als web73:client2 draait en niet als peterpepyco:client2. Ik moet dit verder onderzoeken. Misschien kunnen er naamruimtes worden gebruikt. Maar op dit moment ben ik blij dat het loopt.

Links / credits

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/

Laat een reactie achter

Reageer anoniem of log in om commentaar te geven.

Opmerkingen

Laat een antwoord achter

Antwoord anoniem of log in om te antwoorden.