ISPConfig: Управление Python Flask Docker контейнером как заключенным пользователем "Шелл".
Описанный метод требует, чтобы вы были root, то есть не универсальным, но может быть достаточным, если вы являетесь системным администратором.
![post main image](/static/blog/posts/media/images/blog-post-300-3712-ispconfig-running-a-python-flask-docker-container-as-a-jailed-shell-user.jpg)
Я управляю сервером с 50 сайтами. Вы можете создавать виртуальные среды на ISPConfig сервере и ISPConfig запускать свои приложения отсюда, но некоторое время назад я решил использовать Python Flask их для разработки и производства. staging На это уходит время, но это того стоит. Docker Так держать.
Я воспользуюсь заключенным пользователем Shell для управления контейнером. Причина в том, что когда контейнер прерывает доступ ограничивается правами пользователя Shell в тюрьме, не так ли? См. также Резюме ниже.
Моя система
- ISPConfig3 3.1.13
- Debian 9 (Растягивание)
- MariaDB 10.3
- Nginx 1.10.3
Мой Docker контейнер на ISPConfig
Для производства Python Flask Docker контейнера, основанного на Alpine и содержащего приложение и веб сервер Gunicorn WSGI, используется отображение "томов" для файлов журналов, файлов сеансов и кэш-памяти. Кроме того, он содержит отображение "томов" в статический каталог.
Для разработки и производства я использую файл .dockerignore, чтобы исключить статическую папку из каталога Docker image. Он быстро растет со всеми изображениями. Для развития он нам все равно не нужен, так как мы обслуживаем все, что находится за пределами Docker контейнера. Для производства мы также не хотим, чтобы статическая папка в контейнере оставалась неподвижной. Здесь мы обслуживаем статические элементы не с, а непосредственно с Nginx.
Docker Python Flask Контейнер не содержит базы данных и т.д., но использует ISPConfig сервисы для простой настройки и управления:
- Управление ISPConfig доменом и сайтами (включая шифрование SSL)
- База данных хоста (MariaDB), подключение через разъем ( )
- Почта хоста (Postfix), соединитесь с сервером через port 25
- Веб-сервер хозяина (Nginx), обратный и обслуживающий статический сервер ( )
Обратите внимание, что я собираю Docker image на своей локальной машине, используя ее:
docker save ...
Полученный tar-файл копируется на ISPConfig сервер и распаковывается, см. ниже.
Для настройки под наш сайт мы делаем все как обычно:
- Установка Docker и компоновка (только один раз)
- Добавить домен
- Добавить веб-сайт, установить Позволяет шифровать SSL
- Добавить пользователя базы данных и базы данных
- Добавьте заключенного (!) Пользователя раковины, Chroot Shell: Тюрьма
Каталоги по ISPConfig, пользователям и группам
Когда мы добавили сайт (и создали оболочку), для него был ISPConfig создан пользователь (Linux), мой пользователь Shell:
- Имя пользователя: peterpepepyco
Пользователь и группа linux, см. раздел ISPConfig -> Shell User -> Options, в моем случае:
- Имя пользователя: web73
- Web Group: клиент2
Вы также можете увидеть это, войдя в систему с Shell User и пройдя через некоторые директории, сделав 'ls -n'.
Существует разница между заключенным в тюрьму пользователем Shell и не заключенным в тюрьму пользователем Shell. В обоих случаях базовой директорией является директория:
/var/www/clients/client2/web73
Домашний каталог:
/var/www/clients/client2/web73/home/peterpepyco
и веб-каталог:
/var/www/clients/client2/web73/web
Когда Shell User находится в тюрьме, корень файловой системы меняется на базовый каталог.для получения группы типа Shell User:
groups
который возвращается в моем случае:
client2
Для запуска в Docker качестве другого пользователя нам необходимы идентификатор пользователя, UID и идентификатор группы, GID. Чтобы получить тип UID:
id -u
который возвращается в моем случае 5055, и...:
id -g
который возвращается в моем случае 5006. Есть много способов получить UID и GID. Вы также можете печатать:
cat /etc/passwd
который возвращается:
root:x:0:0:root:/root:/bin/bash
peterpepyco:x:5055:5006:::/bin/bash
и
cat /etc/group
который возвращается:
root:x:0:
client2:x:5006:
Вы также можете создать файл, "echo " > a", а затем сделать 'ls -n' и т.д.
Изменение Nginx конфигурации
ISPConfig Зайдите на сайт и выберите вкладку Параметры. В разделе Nginx Директивы вставить:
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;
}
Обратите внимание, что запросы в порт 8000 передаются в обратном порядке. Это порт, который прослушивает Gunicorn WSGI-сервер в контейнере.
Копирование файлов
В Shell User home я создаю докер директорий, куда копирую сжатый контейнер, файл переменных окружения, docker-compose файлы и базу данных. После копирования каталог выглядит следующим образом:
.
└── 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
Для загрузки базы данных:
mysql -upeterspythonuser -p peterspython2 < peterspython2.dump_20191017
Чтобы загрузить изображение доккера, необходимо, чтобы вы были root:
docker load -i peterspython_image_web_1.283.tar
Следующим шагом будет копирование статической папки из моей локальной системы в папку ISPConfig '/web', см. также выше.
Добавление пользователя, UID и группы, GID, в и Dockerфайл
Я использую Docker-compose для запуска и остановки контейнера, томов карты и т.д. Файл .env содержит ряд переменных конфигурации, которые мы передаем в . Первая часть этого файла:
# 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
...
Первая часть композиционного файла:
# 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:
...
а затем в Dockerдосье:
...
# 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
Запуск контейнера
Опять же, это требует, чтобы вы были корнем:
docker-compose -f docker-compose_base_1.283_production.yml -f docker-compose_production.yml up -d
В результате
Creating network "docker_default" with the default driver
Creating peterspython_container_web ... done
Если контейнер не запускается, проверьте журналы, сообщения. Если он работает, но вы получаете ошибки, вы можете ввести запущенный контейнер, сначала получите идентификатор контейнера:
docker ps
который возвращается:
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
Затем войдите в работающий контейнер:
docker exec -it 292aa9bcecaf sh
Обратите внимание, что мы начинаем с sh, а не с bash, потому что bash нет в Alpine изображении.
Резюме
Это не так уж сложно, когда вы понимаете (часть) Docker и (часть) ISPConfig. Теперь вы можете запускать все, что угодно на ISPConfig сервере.
Я использовал максимум ISPConfig услуг, я счастлив, MariaDB но некоторые люди могут жаловаться, что, например, PostgreSQL не поддерживается. Было бы неплохо добавить PostgreSQL в качестве опции. ISPConfig Это было бы лучше, чем добавить услугу PostgreSQL к контейнеру, увеличив его размер.
Проблема в том, что при загрузке контейнера Docker image , запуске и остановке контейнера мы должны быть суперпользователями, т.е. для каждой Docker команды это требуется, поэтому данный метод не подходит только для некоторых случайных клиентов. Было бы неплохо, если бы поддерживался метод разрешения для каждого сайта и Docker-Compose команд. В контейнерах также находятся порты, которые могут вступать в конфликт с существующими. Это можно решить, назначив диапазон портов для каждого сайта.
Достаточно ли ISPConfig безопасна установка с помощью арестованных учетных данных пользователя Shell для запуска контейнера? Я вижу возможную проблему с использованием UID и запуском корневого контейнера. UID заключенного пользователя Shell User peterpepyco аналогичен UID web73, что означает, что Docker контейнер на самом деле работает как web73:client2, а не как peterpepyco:client2. Я должен разобраться с этим подробнее. Возможно, можно использовать пространство имен. Но на данный момент я счастлив, что он работает.
Ссылки / кредиты
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/
Подробнее
Docker Docker-compose Flask ISPConfig
Недавний
- Скрытие первичных ключей базы данных UUID вашего веб-приложения
- Don't Repeat Yourself (DRY) с Jinja2
- SQLAlchemy, PostgreSQL, максимальное количество строк для user
- Показать значения в динамических фильтрах SQLAlchemy
- Безопасная передача данных с помощью шифрования Public Key и pyNaCl
- rqlite: альтернатива dist с высокой степенью готовности и SQLite
Большинство просмотренных
- Используя Python pyOpenSSL для проверки SSL-сертификатов, загруженных с хоста
- Использование UUID вместо Integer Autoincrement Primary Keys с SQLAlchemy и MariaDb
- Подключение к службе на хосте Docker из контейнера Docker
- Использование PyInstaller и Cython для создания исполняемого файла Python
- SQLAlchemy: Использование Cascade Deletes для удаления связанных объектов
- Flask Удовлетворительный запрос API проверка параметров запроса с помощью схем Маршмэллоу