UMGUM.COM 

Passwork ( Развёртывание в среде исполнения "Docker" web-приложения "Passwork", предназначенного для хранения и управления паролями в корпоративной среде. )

25 сентября 2020  (обновлено 22 марта 2021)

OS: "Linux Debian 9/10", "Linux Ubuntu Server 18/20 LTS".
Apps: "Passwork v4.0.10/4.1.7", "Nginx", PHP, "Phalcon 3.4", "MongoDB", "mSMTP", "Docker", "Docker Compose", LDAP, "IPtables".

Задача: развернуть в среде исполнения "Docker" написанное на "PHP + Phalcon" web-приложение "Passwork", предназначенное для хранения и управления паролями в корпоративной среде (данных много и они должны быть структурированы).

Почему "Passwork"? В 2020-м году мною было проведено исследование интернет-рынка, показавшее, что это оптимальный для наших относительно скромных задач выбор. Понравилась внешняя простота интерфейса пользователя и отсутствие избыточного функционала, нарастающего со временем в других менеджерах паролей. Также на чашу весов повлияло место происхождения - разработчики в российском городе Архангельск. Позже я сильно разочаровался в "Passwork" (вернее в его команде разработки и поддержки), о чём отдельно будет рассказано - в результате чего не рекомендую "Passwork" к использованию.

Последовательность дальнейших действий такова:

1. Подготовка системного окружения (отдельная инструкция);
2. Установка системы контейнеризации "Docker" (отдельная инструкция);
3. Установка сопутствующего ПО и подготовка конфигурации;
4. Загрузка и развёртывание из репозитория приложения "Passwork";
5. Подготовка специализированного docker-образа PHP-интерпретатора для "Passwork";
6. Подготовка конфигурации СУБД "MongoDB";
7. Подготовка конфигурации почтовой подсистемы;
8. Установка и настройка фронтального web-сервера "Nginx";
9. Наладка запуска посредством "Docker Compose" и "Systemd";
10. Подготовка СУБД "MongoDB" и создание БД для "Passwork";
11. Первоначальная настройка web-приложения "Passwork";
12. Ограничение межсетевых взаимодействий посредством "IPtables".


Подготовка несущей файловой структуры для приложений "Passwork".

Заготовим директории для исполняемых скриптов web-сервиса, конфигурационных файлов и журналов событий:

# mkdir -p /var/opt/passwork/log
# mkdir -p /var/opt/passwork/mongodb/conf
# mkdir -p /var/opt/passwork/mongodb/data
# mkdir -p /var/opt/passwork/php/etc
# mkdir -p /var/opt/passwork/www

Загрузка и развёртывание из репозитория приложения "Passwork".

Несмотря на то, что на сайте разработчиков заявлено, что "Passwork" представляет собой полностью "открытое ПО", загрузить его без логина и пароля, выдаваемого индивидуально каждому клиенту только после приобретения лицензии, невозможно:

# cd /usr/src
# git clone https://passwork.download/passwork/passwork.git
# cd ./passwork
# git checkout v4

Копируем в среду исполнения только необходимые файлы из дистрибутива:

# cp -r /usr/src/passwork/app /var/opt/passwork/www/
# cp -r /usr/src/passwork/public /var/opt/passwork/www/

Обращаю внимание, что при дальнейшем обновлении понадобится сохранить от перезаписи две файловые структуры: "./app/config/config.ini" и "./app/keys/*".

Сразу исправляем ACL исполняемых файлов "Passwork":

# find /var/opt/passwork/www -type d -exec chmod 750 {} \;
# find /var/opt/passwork/www -type f -exec chmod 640 {} \;
# chown -R www-data:www-data /var/opt/passwork/www
# chmod -R o-rwx /var/opt/passwork/www

Подготовка специализированного docker-образа PHP-интерпретатора для "Passwork".

В попытках запустить web-сервис "Passwork v4" на комбинации современной "Linux Ubuntu 20.04 LTS", интерпретатора "PHP 7.4" и модуля фреймворка "Phalcon v4" выявлен ряд проблем, главенствующая среди которых та, web-сервис "Passwork v4" написан с использованием устаревшего модуля "Phalcon 3.4", сборка которого уже не поддерживается в системном окружении современных операционных систем, а на свежем "Phalcon 4.0" web-сервис "Passwork v4" не работает (используемые "Passwork v4" классы уже исключены из "Phalcon 4.0", как критически устаревшие). Таким образом, мы вынуждены оставаться пока на "Ubuntu 18.04 LTS", "PHP 7.2" и "Phalcon 3.4".

Собираем docker-образ, с которым в дальнейшем будем запускать "Passwork":

# mkdir -p /usr/local/etc/devops/images
# cd /usr/local/etc/devops/images
# vi ./Dockerfile-php-7.2-fpm-phalcon-3.4

FROM ubuntu:bionic

LABEL maintainer="NSU, Andrey Narozhniy"

ENV HOME /root
ENV DEBIAN_FRONTEND noninteractive

RUN apt-get update && apt-get install --no-install-recommends --no-install-suggests -y apt-utils \
  && apt-get install --no-install-recommends -y ca-certificates gnupg git curl bzip2 file tzdata msmtp \
  && apt-get install --no-install-recommends -y \
    php7.2-fpm php7.2-cgi php7.2-cli php7.2-opcache php7.2-common \
    php7.2-json php7.2-ldap php7.2-xml php7.2-bcmath php7.2-mbstring php7.2-gd php7.2-curl \
    php7.2-intl php7.2-imap php7.2-zip php7.2-bz2 php-imagick php-memcache php-mongodb \
  && apt-get install --no-install-recommends -y php7.2-dev libpcre3-dev php-pear make \
  && pecl channel-update pecl.php.net && pecl install psr \
  && echo "extension=psr.so" > /etc/php/7.2/mods-available/psr.ini \
  && ln -s /etc/php/7.2/mods-available/psr.ini /etc/php/7.2/cgi/conf.d/20-psr.ini \
  && ln -s /etc/php/7.2/mods-available/psr.ini /etc/php/7.2/cli/conf.d/20-psr.ini \
  && ln -s /etc/php/7.2/mods-available/psr.ini /etc/php/7.2/fpm/conf.d/20-psr.ini \
  && cd /usr/src && git clone --branch 3.4.x --depth=1 https://github.com/phalcon/cphalcon.git && cd ./cphalcon/build && ./install && cd ../.. && rm -rf /usr/src/cphalcon \
  && echo "extension=phalcon.so" > /etc/php/7.2/mods-available/phalcon.ini \
  && ln -s /etc/php/7.2/mods-available/phalcon.ini /etc/php/7.2/cgi/conf.d/25-phalcon.ini \
  && ln -s /etc/php/7.2/mods-available/phalcon.ini /etc/php/7.2/cli/conf.d/25-phalcon.ini \
  && ln -s /etc/php/7.2/mods-available/phalcon.ini /etc/php/7.2/fpm/conf.d/25-phalcon.ini \
  && apt-get remove -y php7.2-dev libpcre3-dev php-pear make git \
  && apt-get remove -y apt-utils ca-certificates gnupg \
  && apt-get purge -y --auto-remove \
  && rm -rf /var/lib/apt/lists/* /tmp/* /var/tmp/* \
  && mkdir -p /run/php && chown www-data:www-data /run/php \
  && ln -s /usr/bin/msmtp /usr/sbin/sendmail \
  && ln -sf /dev/stderr /var/log/php7.2-fpm.log

EXPOSE 9000

# in the regular way start PHP-FPM
CMD [ "/usr/sbin/php-fpm7.2", "-F", "-O", "-c", "/etc/php/7.2/fpm/php.ini", "-y", "/etc/php/7.2/fpm/php-fpm.conf" ]

Собираем "образ", явно отключив кеширование на основе слоёв от предыдущих попыток:

# docker build --no-cache --tag selfmade:php-7.2-fpm-phalcon-3.4 --file ./Dockerfile-php-7.2-fpm-phalcon-3.4 . >> ./build-php-7.2-fpm-phalcon-3.4.log

Удостоверяемся в появлении нового docker-образа:

# docker images

Пробуем запустить контейнер с новым образом для того, чтобы проконтролировать изменения внутри него с целью дальнейшей оптимизации:

# docker run -d --rm --name test-php-fpm-phalcon selfmade:php-7.2-fpm-phalcon-3.4
# docker top test-php-fpm-phalcon
# docker diff test-php-fpm-phalcon
# docker logs test-php-fpm-phalcon
# docker stop test-php-fpm-phalcon

Подготовка конфигурации PHP-интерпретатора.

Прежде всего получаем дистрибутивные варианты конфигурационных файлов, используемые внутри docker-образа:

# docker create --name tmp-php-7.2-fpm-phalcon-3.4 selfmade:php-7.2-fpm-phalcon-3.4
# docker cp tmp-php-7.2-fpm-phalcon-3.4:/etc/php/7.2 /var/opt/passwork/php/etc
# docker rm tmp-php-7.2-fpm-phalcon-3.4

Вносим некоторые изменения в конфигурацию PHP-интерпретатора (справка):

# vi /var/opt/passwork/php/etc/7.2/fpm/php.ini

....
; Включаем и донастраиваем PHP-акселератор
opcache.enable = 1
opcache.validate_timestamps = 1
opcache.use_cwd = 1
opcache.revalidate_path = 1
opcache.revalidate_freq = 0
....

Конфигурационный файл FPM-пула полностью переписываем:

# vi /var/opt/passwork/php/etc/7.2/fpm/pool.d/tcp.conf

; Инстанс PHP-FPM, работающий через TCP-подключение
[tcp]

; Указываем пользователя и группу, от имени которых запускается сервис
user = www-data
group = www-data

; Принимаем подключения через TCP
listen = 0.0.0.0:9000

; Деактивируя параметр разрешаем подключение по TCP откуда угодно (подсеть изолирована docker-ом)
;listen.allowed_clients =

; Режим запуска инстанса
pm = dynamic
; Количество процессов, запускаемых при старте PHP-FPM
pm.start_servers = 2
; Максимальное количество процессов, которые могут быть запущены для обработки запросов
pm.max_children = 32
; Параметры количества запущенных неактивных процессов (находящихся в ожидании запросов)
pm.min_spare_servers = 2
pm.max_spare_servers = 5
; Количество запросов, после которого процесс будет перезапущен (для компенсации "утечек памяти" в скриптах)
pm.max_requests = 1024

; Разрешаем чтение системных переменных окружения (default: Yes)
clear_env = No

; Разрешаем подключение к LDAP-серверам ингорируя невозможность проверки SSL-сертификата
env[LDAPTLS_REQCERT] = "allow"

Настройка корректной работы "Passwork" через SSL/TLS.

Современные браузеры, в первую очередь "Google Chrome", при работе с сайтом через SSL-соединение (HTTPS) требуют наличия флагов "Secure" и "SameSite" у cookie-файлов. Соответствующие настройки требуется внести одновременно в конфигурации PHP и непосредственно сайта "Passwork".

Прежде всего явно включаем параметр "Secure" в файле конфигурации PHP:

# vi /var/opt/passwork/php/etc/7.2/fpm/php.ini

....
session.cookie_secure = On
....

Явно "выключаем выключение" поддержки "SameSite" в конфигурации сайта "Passwork":

# vi /var/opt/passwork/www/app/config/config.ini

....
[application]
....
disableSameSiteCookie = Off
....

Подготовка конфигурации СУБД "MongoDB".

Полностью переопределённая предварительная конфигурации СУБД (пока без поддержки аутентификации, которая будет включена в процессе инсталляции "Passwork"):

# vi /var/opt/passwork/mongodb/conf/mongod.conf

net:
  bindIp: 0.0.0.0
  port: 27017
  ipv6: false
storage:
  dbPath: /data/db
  journal:
    enabled: true
systemLog:
  destination: file
  logAppend: true
  path: /var/log/mongodb/mongod.log
  timeStampFormat: iso8601-utc

Подготовка конфигурации почтовой подсистемы.

Устанавливаем утилиту, которую проще всего использовать для пересылки почтовых сообщений через уже имеющийся корпоративный почтовый сервер:

# aptitude install msmtp
# ln -s /usr/bin/msmtp /usr/sbin/sendmail

Готовим файл для журнала событий отправки почтовых сообщений:

# mkdir -p /var/log/msmtp
# touch /var/log/msmtp/msmtp.log
# chown -R www-data:www-data /var/log/msmtp
# chmod -R ugo+rw /var/log/msmtp/msmtp.log

Конфигурационный файл с параметрами утилиты отправки почты "msmtp":

# vi /etc/msmtprc

# Set default values for all following accounts.
defaults
auth off
tls off
logfile /var/log/msmtp/msmtp.log

# Default Account
account default
host mail.example.net
port 25
domain passwork.example.net
from passwork@example.net

Проверяем успешность настройки параметров отправки почты с несущей операционной системы:

# echo "Test" | msmtp --debug --account=default username@example.net

Установка и настройка фронтального web-сервера "Nginx".

Разработчики "Passwork" применяют и предоставляют инструкции только для фронтального web-сервера "Apache". на практике для терминирования клиентских подключений достаточно легковесного "Nginx":

# aptitude install nginx-light

Для последующего включения в "Nginx" современного SSL/TLS и HTTPv2 генерируем DH-файл:

# mkdir -p /etc/ssl/nginx && chown -R root:root /etc/ssl/nginx
# openssl dhparam -out /etc/ssl/nginx/dhparam.2048.pem 2048

Слегка преднастроим web-сервер:

# vi /etc/nginx/nginx.conf

user www-data www-data;
worker_processes auto;
....
http {
  ....
  server_tokens off;
  client_max_body_size 0;
....

Конфигурация принимающего подключения пользователей web-сервера "Nginx" проста и сводится к описанию параметров "проксирования" всех запросов нижележащему web-сервису, запущенному внутри docker-контейнера:

# vi /etc/nginx/sites-available/passwork.example.net.conf

# Описываем подключения к PHP-FCGI
# (с перспективой горизонтального масштабирования)
upstream balance {
  least_conn;
  server localhost:9000;
}

server {
  listen 80;
  server_name passwork.example.net;
  location / { rewrite ^(.+)$ https://$host$1 permanent; }
  #include /etc/nginx/snippets/letsencrypt.conf;
}

server {
  listen 443 ssl http2;
  server_name passwork.example.net;

  access_log /var/opt/passwork/log/nginx_access.log;
  error_log /var/opt/passwork/nginx_error.log;

  # Описываем параметры установления соединений SSL/TLS
  ssl_dhparam /etc/ssl/nginx/dhparam.2048.pem;
  ssl_certificate /etc/ssl/nginx/passwork.example.net.crt;
  ssl_certificate_key /etc/ssl/nginx/passwork.example.net.key;
  ssl_protocols SSLv2 SSLv3 TLSv1 TLSv1.1 TLSv1.2;
  ssl_ciphers HIGH:!aNULL:!MD5;
  ssl_prefer_server_ciphers on;

  # Перестраховываясь указываем ожидаемую перекодировку контента
  charset utf-8;

  # Указываем месторасположение корня web-сервиса
  root /var/opt/passwork/www/public/;
  index index.php;

  # Глобальный обработчик событий отсутствия запрашиваемого файла
  location / {
    try_files $uri $uri/ /index.php?_url=$uri&$args;
  }

  # Блокируем доступ к типовым "закрытым" ресурсам
  location ~ /\. {
    deny all;
  }

  # Не реагируем на неинтересные события загрузок
  location = /(favicon.ico|robots.txt|sitemap.xml) {
    log_not_found off;
    access_log off;
  }

  # Преобразования запросов, специфичные для "Phalcon"
  location ~* /app/([^/]+)/([^/]+)/template\.([a-zA-Z0-9-]+)\.html {
    try_files /app/$1/$2/template.html $uri;
  }
  location ~* /extension/js/([^/]+)/([^/]+)/template\.([a-zA-Z0-9-]+)\.html {
    try_files /extension/js/$1/$2/template.html $uri;
  }

  # Оптимизируем кешированием выдачу "статических" данных
  location ~* \.(js|css|png|jpg|jpeg|gif|ico|html)$ {
    try_files $uri =404;
    expires max;
    log_not_found off;
    access_log off;
  }

  # Обработчик прямых обращений к PHP-скриптам
  location ~ .*\.php$ {
    try_files $uri =404;
    fastcgi_pass localhost:9000;
    include /etc/nginx/fastcgi_params;
    fastcgi_split_path_info ^(.+?\.php)(/.*)$;
    fastcgi_param PATH_INFO $fastcgi_path_info;
    fastcgi_param PATH_TRANSLATED $document_root$fastcgi_path_info;
    fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
  }
}

Удаляем конфигурацию "по умолчанию", активируем новую, проверяем и запускаем в работу:

# rm /etc/nginx/sites-enabled/default
# ln -s /etc/nginx/sites-available/passwork.example.net.conf /etc/nginx/sites-enabled/passwork.example.net.conf
# nginx -t && nginx -s reload

Наладка запуска посредством "Docker Compose".

Создаём директорию для размещения конфигурационных файлов "Docker Compose":

# mkdir /usr/local/etc/compose
# cd /usr/local/etc/compose

Воспользуемся для единственного в нашей схеме конфигурационного файла именем "по умолчанию":

# vi ./docker-compose.yml

version: "3"
services:
  passwork-php:
    container_name: passwork-php
    image: selfmade:php-7.2-fpm-phalcon-3.4
    environment:
      LDAPTLS_REQCERT: "allow"
      TZ: "Asia/Novosibirsk"
    networks:
      passwork:
        aliases:
          - "passwork-php"
    ports:
      - "127.0.0.1:9000:9000"
    volumes:
      - "/etc/msmtprc:/etc/msmtprc:ro"
      - "/var/opt/passwork/php/etc/7.2:/etc/php/7.2:ro"
      - "/var/opt/passwork/www:/var/opt/passwork/www:rw"
      - "/var/opt/passwork/log:/var/log/msmtp:rw"
    tmpfs:
      - "/tmp"

  passwork-db:
    container_name: passwork-db
    image: mongo:4.2-bionic
    entrypoint: ["mongod", "--config", "/etc/mongodb/mongod.conf"]
    environment:
      TZ: "Asia/Novosibirsk"
    networks:
      passwork:
        aliases:
          - "passwork-db"
    ports:
      - "127.0.0.1:27017:27017"
    volumes:
      - "/var/opt/passwork/mongodb/conf:/etc/mongodb:ro"
      - "/var/opt/passwork/mongodb/data:/data/db:rw"
      - "/var/opt/passwork/log:/var/log/mongodb:rw"
    tmpfs:
      - "/tmp"

networks:
  passwork:
    driver: bridge
    internal: false
    ipam:
      driver: default
      config:
        - subnet: 100.127.255.0/24

Запускаем посредством "Docker Compose" пачку контейнеров "Passwork":

# cd /usr/local/etc/compose
# docker-compose up --remove-orphans --build --force-recreate -d

Останавливаем docker-контейнеры, описанные в конфигурации "Docker Compose":

# docker-compose down

Настройка автозагрузки "Docker Compose" посредством "Systemd".

Создаём файл описания параметров запуска и остановки docker-контейнера посредством "Docker Compose" посредством короткоживущего сервиса "Systemd":

# vi /etc/systemd/system/passwork-docker.service

[Unit]
Description=Passwork Apps in Docker Compose Service
Requires=network.target docker.service containerd.service
After=docker.service

[Service]
Type=oneshot
RemainAfterExit=yes
WorkingDirectory=/usr/local/etc/compose

ExecStartPre=-/bin/bash -c 'chown -R www-data:www-data /var/opt/passwork/www'
ExecStartPre=/usr/local/bin/docker-compose -f docker-compose.yml down --remove-orphans
ExecStart=/usr/local/bin/docker-compose -f /usr/local/etc/compose/docker-compose.yml up --remove-orphans --build --force-recreate --detach
#
ExecStop=/usr/local/bin/docker-compose -f /usr/local/etc/compose/docker-compose.yml down

[Install]
WantedBy=multi-user.target

Указываем "Systemd" перечитать и принять новую конфигурацию, а потом явно активируем и запускаем новый сервис:

# systemctl daemon-reload
# systemctl enable passwork-docker.service
# systemctl start passwork-docker

Смотрим журнал событий "Systemd" если "что-то пошло не так":

# systemctl status passwork-docker.service
# journalctl -xe

Подготовка СУБД "MongoDB" и создание БД для "Passwork".

Можно бы извернуться и оперировать с СУБД внутри docker-контейнера вызовом команд из этого же контейнера, но для простоты и унификации процедур резервного копирования в последующем удобнее в несущей операционной системе установить клиентское приложение "MongoDB":

# apt-get install gnupg
# wget -qO - https://www.mongodb.org/static/pgp/server-4.2.asc | sudo apt-key add -
# echo "deb [ arch=amd64 ] https://repo.mongodb.org/apt/ubuntu bionic/mongodb-org/4.2 multiverse" > /etc/apt/sources.list.d/mongodb-org-4.2.list
# apt-get update
# apt-get install mongodb-org-tools mongodb-org-shell

Подключаемся к запущенной внутри docker-контейнера, прослушивающей на локальной петле порт по умолчанию (TCP:27017), СУБД "MongoDB" и задаём пароль для учётной записи суперпользователя БД:

$ mongo --quiet
> use admin
> db.createUser( { user: "root", pwd: "rootPassword", roles: [ { role:"root", db:"admin" } ] } )
> exit

Включаем в конфигурации "MongoDB" поддержку аутентификации:

# vi /var/opt/passwork/mongodb/conf/mongod.conf

....
security:
  authorization: enabled
....

Перезапускаем контейнер с СУБД:

# docker-compose stop passwork-db
# docker-compose start passwork-db

Аналогично тому, как это сделали выше, создаём "базу данных" для "Passwork" и учётную запись для подключения к таковой:

$ mongo --quiet --port 27017 -u "root" -p --authenticationDatabase "admin"
> use pwbox
> db.createUser( { user: "pwbox", pwd: "userPassword", roles: [ { role: "readWrite", db: "pwbox" } ] } )
> show users
> exit

Проверяем, возможно ли подключение к свежесозданной БД:

$ mongo localhost/pwbox -u pwbox -p
> show collections
> db.init.find()
> exit

Создаём необходимые для работы "Passwork" структуры путём загрузки из заготовленного разработчиками "дампа":

$ mongorestore -h localhost -u pwbox --db=pwbox --drop /usr/src/passwork/dump/pwbox

Профилактически защищаем файлы СУБД от доступа посторонних:

# chown -R root:root /var/opt/passwork/mongodb
# chmod -R o-rwx /var/opt/passwork/mongodb

Конфигурирование web-приложения "Passwork".

Воспользуемся заготовленным разработчиками примерным конфигурационным файлом:

# cp /var/opt/passwork/www/app/config/config.example.ini /var/opt/passwork/www/app/config/config.ini

Сгенерируем "ключевую последовательность" (параметр "[crypt] secret" конфигурационного файла), которая в дальнейшем будет использована для шифрования паролей в БД "Passwork" (естественно, этот секретный пароль крайне важен и с его утратой расшифровать сохранённые в "MongoDB" пароли будет абсолютно невозможно):

# apt-get install pwgen
# pwgen -cns 32 1

Названия параметров конфигурационного файла "Passwork" достаточно говорящие, чтобы не нужно было их назначение как-то отдельно описывать:

# vi /var/opt/passwork/www/app/config/config.ini

[crypt]
secret = ***

[application]
domain = https://passwork.example.net
noreplyEmail = passwork@example.net
lang = en
superAdminEmail = admin@example.net
csrf = On
override = Off
randomPasswordLength = 10;
globalGroupNames = On;
forced2fa = Off;
hash = sha256;
searchSalt =
disableSameSiteCookie = Off

[ban]
time =  180
count = 7
interval = 60

[mongo]
connectionString = mongodb://passwork-db:27017/pwbox?authSource=pwbox
dbname = pwbox
useCreds = true
username = pwbox
password = ***

Профилактически закрываем конфигурационный файл с паролями от доступа посторонних:

# chown -R www-data:www-data /var/opt/passwork/www/app/config/config.ini
# chmod -R o-rwx /var/opt/passwork/www/app/config/config.ini

Первоначальная настройка web-приложения.

При первом обращении к ещё не настроенному "Passwork" в web-интерфейсе потребуется указать логин и пароль для создания суперпользователя web-сервиса, а также загрузить файлы лицензионных ключей "*.lic" и "reginfo.json", которые впоследствии можно будет найти в директории "/var/opt/passwork/www/app/keys".

Настройка подключения к внешнему LDAP/AD для аутентификации.

В "Passwork" весьма неудобная и нелогичная интеграция с внешними сервисами аутентификации - худшая из всех, с которыми мне приходилось доселе иметь дело.

Во-первых, параметры и состояния самым непонятным образом кешируются на стороне web-браузера администратора. Ошибка на любом этапе часто влечёт необходимость возвращаться к верхнему уровню меню настроек, обновлять web-страницу принудительно, желательно со сбросом кеша браузера. Впрочем, попыток через десять настроить интеграцию с LDAP-сервисом в "Passwork" получается.

Во-вторых, параметры конфигурирования не разнесены по соответствующим функциональным блокам, отчего для настройки интеграции приходится скакать туда-сюда, без всякого смысла.

И пароль подключения служебной учётной записи к LDAP-серверу не сохраняется, запрашивается каждый раз при подключении нового пользователя - неудобно.

Кроме того, уже в процессе эксплуатации было обнаружено, что при неполной настройке LDAP-аутентификации в "Passwork" любой существующий пользователь проходит аутентификацию с ЛЮБЫМ паролем. Проистекает это из неверной логики настройки LDAP-аутентификации в "Passwork" и отсутствия проверки корректности логина и пароля при аутентификации. В течении МЕСЯЦА я писал письма технической поддержке "Passwork", детально описывая условия, при которых уязвимость проявляется, на что мне вначале отвечали в стиле "это не баг, а фича", и лишь моя назойливость через пять-семь писем и звонок менеджеру по продажам вынудила их признать, что проблема есть и они будут с ней заниматься.

Думаю, понятно теперь, поэтому я здесь даже не хочу приводить примеры конфигурирования интеграции "Passwork" с LDAP-сервисом - приложение должно вызреть до уровня, когда мне захочется рекомендовать его использование.

Выгрузка резервной копии данных из "MongoDB".

Конечно, все важные сервисы должны быть подключены к централизованной системе автоматизированного резервного копирования. При этом для особо чувствительной информации (вроде всех паролей всех сервисов) хочется иметь дополнительный ручной инструмент, принцип работы которого очевиден и результат действий которым можно легко проверить. Рассмотрим два метода простой выгрузки данных из БД "Passwork":

Выгрузка "бинарного дампа" (быстрого, но не позволяющего просматривать данные без восстановления их в СУБД):

# mkdir -p /var/backups/mongodump
# mongodump --db=pwbox --out /var/backups/mongodump

Как вариант, можно экспортировать интересующие нас данные в виде набора человеко-читаемых JSON-файлов. Для этого напишем простейший bash-скрипт:

# vi /usr/local/bin/mongoexport-all.sh && chmod +x /usr/local/bin/mongoexport-all.sh

#!/bin/bash

DB=$1
COLLECTIONS=$(mongo localhost:27017/$DB --quiet --eval "db.getCollectionNames()" | sed 's/[^_[:alnum:]]/ /g')

for COLLECTION in ${COLLECTIONS}; do
  mongoexport -d ${DB} -c ${COLLECTION} -o ./${COLLECTION}.json
done

exit $?

Перед использованием скрипта переходим в директорию, в которой желаем получить файлы резервной копии и запускаем скрипт, как таковой, указав имя целевой БД:

# mkdir -p /var/backups/mongoexport
# cd /var/backups/mongoexport
# /usr/local/bin/mongoexport-all.sh pwbox

Ограничение межсетевых взаимодействий посредством "IPtables".

Прежде чем запускать в эксплуатацию web-сервис, хранящий в себе весьма чувствительную к компрометации информацию, считаю полезным по возможности изолировать его в локальной сети предприятия, запретив как обращения к нему извне, так и заблокировав возможность установления соединений от самого сервиса за пределы локальной сети предприятия. Очевидно, что утечка возможна и через клиентскую часть приложения (JS, "Angular"), но давайте бороться с тем, что нам под силу.

Обращаю внимание, что используемая система контейнеризации приложений "Docker" распределяет трафик между своими контейнерами посредством "iptables", а значит нужно быть особо внимательным, не зачищая огульно правила, а лишь модифицируя таблицы обработки трафика так, чтобы не повредить работе базовых сервисов.

Прежде всего следует ознакомиться с существующей схемой управления сетевым трафиком:

# iptables -L -n -v --line-numbers
# iptables -t nat -L -n -v --line-numbers
# iptables -t mangle -L -n -v --line-numbers

Пишем простой bash-скрипт, формирующий дополнительные правила фильтрации, если таковые отсутствуют (что делает запуск скрипта чуть безопаснее):

# cd /usr/local/etc
# vi ./set-iptables-rules.sh && chmod ug+x ./set-iptables-rules.sh

#!/bin/bash

# Контролируемый сетевой интерфейс
# (используемый по умолчанию для исходящего трафика)
# IFOUT="eth0"
IFOUT="$(ip -4 route ls | grep default | grep -Po '(?<=dev )(\S+)')"

# Предохранение от сбоя выявления интерфейса
[ -z "${IFOUT}" ] && exit 1

# Создаём цепочку правил только для "Passwork"
iptables -n --list PASSWORK >/dev/null 2>&1 \
  || iptables -N PASSWORK

# Внедряем свою цепь правил в глобальную для транзитного трафика
iptables -C FORWARD -j PASSWORK 2>/dev/null \
  || iptables -I FORWARD 1 -j PASSWORK

# Внедряем свою цепь правил в глобальную для исходящего трафика
iptables -C OUTPUT -j PASSWORK 2>/dev/null \
  || iptables -I OUTPUT 1 -j PASSWORK

# Временно удаляем финальное запретительное правило для исходящих соединений
iptables --check PASSWORK -o ${IFOUT} -j REJECT -m comment --comment "Reject all unapproved outgoing connections" 2>/dev/null \
  && iptables -D PASSWORK -o ${IFOUT} -j REJECT -m comment --comment "Reject all unapproved outgoing connections"

# Одним из первых правил разрешаем прохождение пакетов в уже установленных соединениях
# (исключение избыточной аналитики существенно снижает нагрузку на защитный экран)
iptables --check PASSWORK -o ${IFOUT} -m conntrack --ctstate ESTABLISHED,RELATED -j ACCEPT 2>/dev/null \
  || iptables -A PASSWORK -o ${IFOUT} -m conntrack --ctstate ESTABLISHED,RELATED -j ACCEPT

# Разрешаем выход в локальные сети
iptables --check PASSWORK -o ${IFOUT} -d 10.0.0.0/8 -j ACCEPT -m comment --comment "LAN" 2>/dev/null \
  || iptables -A PASSWORK -o ${IFOUT} -d 10.0.0.0/8 -j ACCEPT -m comment --comment "LAN"
iptables --check PASSWORK -o ${IFOUT} -d 172.16.0.0/12 -j ACCEPT -m comment --comment "LAN" 2>/dev/null \
  || iptables -A PASSWORK -o ${IFOUT} -d 172.16.0.0/12 -j ACCEPT -m comment --comment "LAN"
iptables --check PASSWORK -o ${IFOUT} -d 192.168.0.0/16 -j ACCEPT -m comment --comment "LAN" 2>/dev/null \
  || iptables -A PASSWORK -o ${IFOUT} -d 192.168.0.0/16 -j ACCEPT -m comment --comment "LAN"

# (optional) Разрешаем обращения к DNS-серверам за пределами локальной сети
#iptables --check PASSWORK -o ${IFOUT} -p UDP -d 8.8.8.8/32 --dport 53 -j ACCEPT -m comment --comment "DNS" 2>/dev/null \
#  || iptables -A PASSWORK -o ${IFOUT} -p UDP -d 8.8.8.8/32 --dport 53 -j ACCEPT -m comment --comment "DNS"
#iptables --check PASSWORK -o ${IFOUT} -p TCP -d 8.8.8.8/32 --dport 53 -j ACCEPT -m comment --comment "DNS" 2>/dev/null \
#  || iptables -A PASSWORK -o ${IFOUT} -p TCP -d 8.8.8.8/32 --dport 53 -j ACCEPT -m comment --comment "DNS"

# (optional) Разрешаем обращения к почтовым серверам за пределами локальной сети
iptables --check PASSWORK -o ${IFOUT} -p TCP -d 1.2.3.4/32 --dport 25 -j ACCEPT -m comment --comment "SMTP mail.example.net" 2>/dev/null \
  || iptables -A PASSWORK -o ${IFOUT} -p TCP -d 1.2.3.4/32 --dport 25 -j ACCEPT -m comment --comment "SMTP mail.example.net"

# (optional) разрешаем обращения к серверам обновлений для ОС и приложений
iptables --check PASSWORK -o ${IFOUT} -p TCP -d 91.189.88.0/24 --dport 80 -j ACCEPT -m comment --comment "APT security.ubuntu.com & us.archive.ubuntu.com" 2>/dev/null \
  || iptables -A PASSWORK -o ${IFOUT} -p TCP -d 91.189.88.0/24 --dport 80 -j ACCEPT -m comment --comment "APT security.ubuntu.com & us.archive.ubuntu.com"
iptables --check PASSWORK -o ${IFOUT} -p TCP -d 91.189.91.0/24 --dport 80 -j ACCEPT -m comment --comment "APT security.ubuntu.com & us.archive.ubuntu.com" 2>/dev/null \
  || iptables -A PASSWORK -o ${IFOUT} -p TCP -d 91.189.91.0/24 --dport 80 -j ACCEPT -m comment --comment "APT security.ubuntu.com & us.archive.ubuntu.com"
#
iptables --check PASSWORK -o ${IFOUT} -p TCP -d 52.85.47.0/24 --dport 443 -j ACCEPT -m comment --comment "APT download.docker.com & repo.mongodb.org" 2>/dev/null \
  || iptables -A PASSWORK -o ${IFOUT} -p TCP -d 52.85.47.0/24 --dport 443 -j ACCEPT -m comment --comment "APT download.docker.com & repo.mongodb.org"
iptables --check PASSWORK -o ${IFOUT} -p TCP -d 52.85.115.0/24 --dport 443 -j ACCEPT -m comment --comment "APT download.docker.com & repo.mongodb.org" 2>/dev/null \
  || iptables -A PASSWORK -o ${IFOUT} -p TCP -d 52.85.115.0/24 --dport 443 -j ACCEPT -m comment --comment "APT download.docker.com & repo.mongodb.org"

# Явно отвергаем все неразрешённые ранее исходящие соединения
iptables --check PASSWORK -o ${IFOUT} -j REJECT -m comment --comment "Reject all unapproved outgoing connections" 2>/dev/null \
  || iptables -A PASSWORK -o ${IFOUT} -j REJECT -m comment --comment "Reject all unapproved outgoing connections"

exit $?

Запускаем скрипт и чуть спокойнее спим:

# /usr/local/etc/set-iptables-rules.sh

Учитывая то, что используемый нами "Docker" распределяет трафик между своими контейнерами посредством "iptables", для сохранения вышеописанных правил фильтрации мы не можем применить инструмент вроде "iptables-persistent", так как при запуске несущей операционной системы он будет восстанавливать все предыдущие состояния, что повредит подсистеме "Docker", вероятно уже до этого сформировавшей свои таблицы управления трафика.

Нужно сделать так, чтобы скрипт добавления наших правил запускался автоматически после старта операционной системы и подсистемы контейнеризации "Docker". Ранее (старый я) мы это делали через скрипт "/etc/rc.local" подсистемы "System-V", но с приходом эпохи "Systemd" этот метод не рекомендуется, и придётся идти чуть более сложным путём с регистрацией короткоживущей службы этого самого "Systemd".

# vi /etc/systemd/system/set-iptables-rules.service

[Unit]
Description=Service script of adding rules for filtering of network traffic
Requires=network.target docker.service containerd.service
After=docker.service

[Service]
ExecStart=/usr/local/etc/set-iptables-rules.sh
Type=oneshot
RemainAfterExit=yes

[Install]
WantedBy=default.target

# systemctl daemon-reload
# systemctl enable set-iptables-rules.service
# systemctl start set-iptables-rules.service
# systemctl status set-iptables-rules.service


Заметки и комментарии к публикации:


Оставьте свой комментарий ( выразите мнение относительно публикации, поделитесь дополнительными сведениями или укажите на ошибку )