Applications: "Nginx", PHP-FPM, "NodeJS", "MySQL", "MongoDB", "Memcached", "mSMTP", "inCron", "Supervisord" и "Bash".
Задача: автоматизировать рутинные процедуры создания новых "сводных площадок" групп сайтов и сайтов как таковых, поэтапно описанные в предыдущей основной инструкции по развёртыванию web-сервиса.
Последовательность действий по созданию площадки web-сайта такова:
1. Создание "сводной площадки" группы сайтов.
2. Создание площадки web-сайта.
3. Создание (опционально) БД "MySQL".
4. Создание (опционально) БД "MongoDB".
5. Создание (опционально) аккаунта web-разработчика.
2. Создание площадки web-сайта.
3. Создание (опционально) БД "MySQL".
4. Создание (опционально) БД "MongoDB".
5. Создание (опционально) аккаунта web-разработчика.
Я считаю, что вспомогательные скрипты должны писаться так, чтобы их запуск не вредил уже имеющейся конфигурации, которую возможно создали или доработали вручную. Практика годичной эксплуатации показала, что в данном случае мне это удалось - приводимые здесь скрипты лишь дополняют недостающее и профилактически поправляют права доступа к файлам, если структура web-площадки уже сформирована.
В последнее время я приспособился хранить свои инструменты в отдельной от системного окружения директории "/usr/local/etc" - поступим так и сейчас - создаём директорию для Bash-скриптов создания структур web-сайтов и шаблонов конфигурационных файлов для таковых:
# mkdir -p /usr/local/etc/webman
# chown -R root:root /usr/local/etc/webman
# chmod -R o-rwx /usr/local/etc/webman
# chown -R root:root /usr/local/etc/webman
# chmod -R o-rwx /usr/local/etc/webman
Каждую операцию в скриптах я постарался подробно прокомментировать и в дополнительных сопутствующих пояснениях не вижу необходимости.
Скрипт создания "сводной площадки" группы сайтов.
# vi /usr/local/etc/webman/addwebgroup.sh && chmod ug+x /usr/local/etc/webman/addwebgroup.sh
#!/bin/bash
PHPCONF="/etc/php/7.2"
WEBROOT="/var/www"
GROUP=${1}
LOG="$(dirname ${0})/add.log"
DATE=$(date +"%Y-%m-%d %H:%M:%S")
# Проверяем наличие ожидаемых приложений и утилит
[ -x "$(command -v php-fpm7.2)" ] && [ -x "$(command -v supervisorctl)" ] && [ -x "$(command -v incrond)" ] && [ -x "$(command -v visudo)" ] && [ -x "$(command -v setfacl)" ] || { echo "Не обнаружен необходимый для работы набор приложений и утилит. Операция создания группы сайтов прервана."; exit 1; }
# Проверяем корректность вводимых данных и выводим подсказку в случае необходимости
# (если "группа сайтов" уже имеется - ничего страшного - профилактически перезаписываем и обновляем права доступа структуры директорий)
#
[ ! "${1}" ] && { echo "Ожидаемый формат: ${0} \"group\""; echo "Не указано название \"группы сайтов\". Операция создания \"группы сайтов\" прервана."; exit 1; }
# Пресекаем использование зарезервированных ресурсов
if [[ "${GROUP}" = "cgi-bin" || "${GROUP}" = "letsencrypt" ]] ; then
echo "В качестве имени \"группы сайтов\" указано название зарезервированного ресурса. Операция создания \"группы сайтов\" прервана."; exit 1;
fi
echo >> "${LOG}"
echo "${DATE}:" >> "${LOG}"
# В профилактических целях пытаемся воссоздать корневую директорию ресурсов
mkdir -p ${WEBROOT}
chown root:root ${WEBROOT}
chmod go-w ${WEBROOT}
chmod go+rx ${WEBROOT}
#
setfacl --no-mask --set default:user::rwX,default:group::rwX,default:other:--- ${WEBROOT}
setfacl --no-mask --modify group::rX,other:rX ${WEBROOT}
# Создаём файловую структуру "группы сайтов"
mkdir -p ${WEBROOT}/${GROUP}
#
setfacl --no-mask --modify user::rX,group::rX ${WEBROOT}/${GROUP}
setfacl --no-mask --modify user:www-data:X ${WEBROOT}/${GROUP}
#
mkdir -p ${WEBROOT}/${GROUP}/conf
mkdir -p ${WEBROOT}/${GROUP}/home
mkdir -p ${WEBROOT}/${GROUP}/log
mkdir -p ${WEBROOT}/${GROUP}/mnt
mkdir -p ${WEBROOT}/${GROUP}/tmp
# Проверяем успешность создания опорной файловой структуры
if [ -d "${WEBROOT}/${GROUP}" ] ; then
echo "Успешно создана опорная файловая структура для группы сайтов \"${GROUP}\"." | tee -a "${LOG}"
else
echo "В процессе создания файловой структуры случился сбой. Операция добавления группы сайтов \"${GROUP}\" прервана." | tee -a "${LOG}"
exit 1
fi
# Добавляем опорного пользователя "группы сайтов"
groupadd --force www-${GROUP} > /dev/null 2>&1
useradd --shell /bin/false --no-create-home --home-dir ${WEBROOT}/${GROUP}/home --gid www-${GROUP} www-${GROUP} > /dev/null 2>&1
# Переводим файловую структуру "группы сайтов" во владение опорного пользователя
chown -R www-${GROUP}:www-${GROUP} ${WEBROOT}/${GROUP}
# Удовлетворяем требования OpenSSH-сервера (поддержка "chroot" в SFTP)
chown root:www-${GROUP} ${WEBROOT}/${GROUP}
chmod g+rx ${WEBROOT}/${GROUP}
# Добавляем пользователя, от имени которого будут осуществляться операции с Git-репозитариями
useradd --shell /usr/bin/git-shell --create-home --home-dir /home/git-${GROUP} --gid www-${GROUP} git-${GROUP} > /dev/null 2>&1
# Подготовим место для SSH-ключей пользователя операций Git-репозитариев
mkdir -p /home/git-${GROUP}/.ssh
touch /home/git-${GROUP}/.ssh/authorized_keys
chown -R git-${GROUP}:www-${GROUP} /home/git-${GROUP}/.ssh
chmod -R go-rwx /home/git-${GROUP}/.ssh
# Проверяем в "sshd_config" наличие правил для "группы сайтов" и добавляем их при необходимости
if [[ "$(grep -i -c -e "match\b\sgroup\b\swww-${GROUP}\b" "/etc/ssh/sshd_config")" -eq "0" ]] ; then
source "$(dirname ${0})/sftp.sh.snippet"
[ "${?}" -ne "0" ] && { echo "Template file SSHd is missing or wrong! Операция добавления группы сайтов \"${GROUP}\" прервана." | tee -a "${LOG}"; exit 1; }
# Тестируем конфигурацию и перезапускаем сервис
sshd -t
if [ "${?}" -eq "0" ] ; then
/etc/init.d/ssh reload
echo "В конфигурационный файл SSHd успешно внесены настройки для группы сайтов \"${GROUP}\"." | tee -a "${LOG}"
else
echo "На этапе внесения изменений в файл конфигурации SSHd \"/etc/ssh/sshd_config\" случился сбой. Операция добавления группы сайтов \"${GROUP}\" прервана." | tee -a "${LOG}"
exit 1
fi
fi
# Проверяем наличие настроек PHP-FPM для "группы сайтов" и добавляем их при необходимости
if [ ! -f ${PHPCONF}/fpm/pool.d/${GROUP}.conf ] ; then
mkdir -p ${PHPCONF}/fpm/pool.d
source "$(dirname ${0})/php-fpm.sh.snippet"
[ "${?}" -ne "0" ] && { echo "Template file PHP-FPM is missing or wrong! Операция добавления группы сайтов \"${GROUP}\" прервана." | tee -a "${LOG}"; exit 1; }
# Тестируем конфигурацию и перезапускаем сервис
php-fpm7.2 -t --fpm-config ${PHPCONF}/fpm/pool.d/${GROUP}.conf
if [ "${?}" -eq "0" ] ; then
/etc/init.d/php7.2-fpm reload
echo "В конфигурацию "пулов" PHP-FPM успешно внесены настройки для группы сайтов \"${GROUP}\"." | tee -a "${LOG}"
else
echo "На этапе внесения изменений в файл конфигурации PHP-FPM \"${PHPCONF}/fpm/pool.d/${GROUP}.conf\" случился сбой. Операция добавления группы сайтов \"${GROUP}\" прервана." | tee -a "${LOG}"
exit 1
fi
else
echo "Обнаруженный имеющийся файл конфигурации PHP-FPM \"${PHPCONF}/fpm/pool.d/${GROUP}.conf\" оставлен без изменений." | tee -a "${LOG}"
fi
# Проверяем наличие настроек "Supervisor + Memcached" для "группы сайтов" и добавляем их при необходимости
if [ ! -f /etc/supervisor/conf.d/memcached-${GROUP}.conf ] ; then
mkdir -p /etc/supervisor/conf.d
source "$(dirname ${0})/memcached.sh.snippet"
[ "${?}" -ne "0" ] && { echo "Template file Supervisor-Memcached is missing or wrong! Операция добавления группы сайтов \"${GROUP}\" прервана." | tee -a "${LOG}"; exit 1; }
# Применяем изменения конфигурации
supervisorctl reread > /dev/null 2>&1 && supervisorctl update > /dev/null 2>&1
if [ "${?}" -eq "0" ] ; then
echo "В конфигурацию \"Supervisor-Memcached\" успешно внесены настройки для группы сайтов \"${GROUP}\"." | tee -a "${LOG}"
else
echo "На этапе внесения изменений в файл конфигурации \"/etc/supervisor/conf.d/memcached-${GROUP}.conf\" для \"Supervisor-Memcached\" случился сбой. Операция добавления группы сайтов \"${GROUP}\" прервана." | tee -a "${LOG}"
exit 1
fi
else
echo "Обнаруженный имеющийся файл конфигурации Supervisor-Memcached \"/etc/supervisor/conf.d/memcached-${GROUP}.conf\" оставлен без изменений." | tee -a "${LOG}"
fi
# Если поддерживается экспорт "кронтабов" посредством "inCron", то добавляем в отслеживание новую таблицу
if [ -d "/etc/incron.d" ] ; then
touch "/etc/incron.d/web-crontab-export"
if [[ "$(grep -i -c -e "${WEBROOT}/${GROUP}/conf\b" "/etc/incron.d/web-crontab-export")" -eq "0" ]] ; then
source "$(dirname ${0})/incron.sh.snippet" > /dev/null 2>&1
[ "${?}" -ne "0" ] && { echo "Template file inCron is missing or wrong!" | tee -a "${LOG}"; exit 1; }
# Создаём файл "кронтаба" в файловой структуре "группы сайтов"
crontab -u www-${GROUP} -l > ${WEBROOT}/${GROUP}/conf/crontab 2> /dev/null
chown www-${GROUP}:www-${GROUP} ${WEBROOT}/${GROUP}/conf/crontab
chmod g+w ${WEBROOT}/${GROUP}/conf/crontab
chmod o-rwx ${WEBROOT}/${GROUP}/conf/crontab
echo "В конфигурационный файл inCron внесены настройки автоматического экспорта \"${WEBROOT}/${GROUP}/conf/crontab\" для группы сайтов \"${GROUP}\"." | tee -a "${LOG}"
else
echo "Обнаруженная имеющаяся конфигурация "inCron" оставлена без изменений." | tee -a "${LOG}"
fi
fi
# Разрешаем web-серверу переключаться в контекст пользователя, от имени которого будут осуществляться операции с Git-репозитариями
touch /etc/sudoers.d/web-git-deploy-users
if [[ $(grep -i -c -e "^www-data\b\s.*git-${GROUP}:www-${GROUP}.*NOPASSWD:.*/usr/bin/git\b" "/etc/sudoers.d/web-git-deploy-users") -eq "0" ]] ; then
# В профилактических целях корректируем ограничения доступа к файлу конфигурации
chown root:root /etc/sudoers.d/web-git-deploy-users
chmod go-rwx /etc/sudoers.d/web-git-deploy-users
# Вносим добавлением к возможно уже имеющимся разрешительное правило
echo "www-data ALL=(git-${GROUP}:www-${GROUP}) NOPASSWD: /usr/bin/git" >> /etc/sudoers.d/web-git-deploy-users
# Тестируем конфигурацию
visudo -cf /etc/sudoers.d/web-git-deploy-users
if [ "${?}" -eq "0" ] ; then
echo "В конфигурационный файл SUDO успешно внесены разрешительные правила \"автоматизации деплоя\" для группы сайтов \"${GROUP}\"." | tee -a "${LOG}"
else
echo "На этапе внесения изменений в файл конфигурации SUDO \"/etc/sudoers.d/web-git-deploy-users\" случился сбой. Операция добавления группы сайтов \"${GROUP}\" прервана." | tee -a "${LOG}"; exit 1;
fi
else
echo "Обнаруженная имеющаяся конфигурация \"автоматизации деплоя\" в SUDO оставлена без изменений." | tee -a "${LOG}"
fi
exit ${?}
PHPCONF="/etc/php/7.2"
WEBROOT="/var/www"
GROUP=${1}
LOG="$(dirname ${0})/add.log"
DATE=$(date +"%Y-%m-%d %H:%M:%S")
# Проверяем наличие ожидаемых приложений и утилит
[ -x "$(command -v php-fpm7.2)" ] && [ -x "$(command -v supervisorctl)" ] && [ -x "$(command -v incrond)" ] && [ -x "$(command -v visudo)" ] && [ -x "$(command -v setfacl)" ] || { echo "Не обнаружен необходимый для работы набор приложений и утилит. Операция создания группы сайтов прервана."; exit 1; }
# Проверяем корректность вводимых данных и выводим подсказку в случае необходимости
# (если "группа сайтов" уже имеется - ничего страшного - профилактически перезаписываем и обновляем права доступа структуры директорий)
#
[ ! "${1}" ] && { echo "Ожидаемый формат: ${0} \"group\""; echo "Не указано название \"группы сайтов\". Операция создания \"группы сайтов\" прервана."; exit 1; }
# Пресекаем использование зарезервированных ресурсов
if [[ "${GROUP}" = "cgi-bin" || "${GROUP}" = "letsencrypt" ]] ; then
echo "В качестве имени \"группы сайтов\" указано название зарезервированного ресурса. Операция создания \"группы сайтов\" прервана."; exit 1;
fi
echo >> "${LOG}"
echo "${DATE}:" >> "${LOG}"
# В профилактических целях пытаемся воссоздать корневую директорию ресурсов
mkdir -p ${WEBROOT}
chown root:root ${WEBROOT}
chmod go-w ${WEBROOT}
chmod go+rx ${WEBROOT}
#
setfacl --no-mask --set default:user::rwX,default:group::rwX,default:other:--- ${WEBROOT}
setfacl --no-mask --modify group::rX,other:rX ${WEBROOT}
# Создаём файловую структуру "группы сайтов"
mkdir -p ${WEBROOT}/${GROUP}
#
setfacl --no-mask --modify user::rX,group::rX ${WEBROOT}/${GROUP}
setfacl --no-mask --modify user:www-data:X ${WEBROOT}/${GROUP}
#
mkdir -p ${WEBROOT}/${GROUP}/conf
mkdir -p ${WEBROOT}/${GROUP}/home
mkdir -p ${WEBROOT}/${GROUP}/log
mkdir -p ${WEBROOT}/${GROUP}/mnt
mkdir -p ${WEBROOT}/${GROUP}/tmp
# Проверяем успешность создания опорной файловой структуры
if [ -d "${WEBROOT}/${GROUP}" ] ; then
echo "Успешно создана опорная файловая структура для группы сайтов \"${GROUP}\"." | tee -a "${LOG}"
else
echo "В процессе создания файловой структуры случился сбой. Операция добавления группы сайтов \"${GROUP}\" прервана." | tee -a "${LOG}"
exit 1
fi
# Добавляем опорного пользователя "группы сайтов"
groupadd --force www-${GROUP} > /dev/null 2>&1
useradd --shell /bin/false --no-create-home --home-dir ${WEBROOT}/${GROUP}/home --gid www-${GROUP} www-${GROUP} > /dev/null 2>&1
# Переводим файловую структуру "группы сайтов" во владение опорного пользователя
chown -R www-${GROUP}:www-${GROUP} ${WEBROOT}/${GROUP}
# Удовлетворяем требования OpenSSH-сервера (поддержка "chroot" в SFTP)
chown root:www-${GROUP} ${WEBROOT}/${GROUP}
chmod g+rx ${WEBROOT}/${GROUP}
# Добавляем пользователя, от имени которого будут осуществляться операции с Git-репозитариями
useradd --shell /usr/bin/git-shell --create-home --home-dir /home/git-${GROUP} --gid www-${GROUP} git-${GROUP} > /dev/null 2>&1
# Подготовим место для SSH-ключей пользователя операций Git-репозитариев
mkdir -p /home/git-${GROUP}/.ssh
touch /home/git-${GROUP}/.ssh/authorized_keys
chown -R git-${GROUP}:www-${GROUP} /home/git-${GROUP}/.ssh
chmod -R go-rwx /home/git-${GROUP}/.ssh
# Проверяем в "sshd_config" наличие правил для "группы сайтов" и добавляем их при необходимости
if [[ "$(grep -i -c -e "match\b\sgroup\b\swww-${GROUP}\b" "/etc/ssh/sshd_config")" -eq "0" ]] ; then
source "$(dirname ${0})/sftp.sh.snippet"
[ "${?}" -ne "0" ] && { echo "Template file SSHd is missing or wrong! Операция добавления группы сайтов \"${GROUP}\" прервана." | tee -a "${LOG}"; exit 1; }
# Тестируем конфигурацию и перезапускаем сервис
sshd -t
if [ "${?}" -eq "0" ] ; then
/etc/init.d/ssh reload
echo "В конфигурационный файл SSHd успешно внесены настройки для группы сайтов \"${GROUP}\"." | tee -a "${LOG}"
else
echo "На этапе внесения изменений в файл конфигурации SSHd \"/etc/ssh/sshd_config\" случился сбой. Операция добавления группы сайтов \"${GROUP}\" прервана." | tee -a "${LOG}"
exit 1
fi
fi
# Проверяем наличие настроек PHP-FPM для "группы сайтов" и добавляем их при необходимости
if [ ! -f ${PHPCONF}/fpm/pool.d/${GROUP}.conf ] ; then
mkdir -p ${PHPCONF}/fpm/pool.d
source "$(dirname ${0})/php-fpm.sh.snippet"
[ "${?}" -ne "0" ] && { echo "Template file PHP-FPM is missing or wrong! Операция добавления группы сайтов \"${GROUP}\" прервана." | tee -a "${LOG}"; exit 1; }
# Тестируем конфигурацию и перезапускаем сервис
php-fpm7.2 -t --fpm-config ${PHPCONF}/fpm/pool.d/${GROUP}.conf
if [ "${?}" -eq "0" ] ; then
/etc/init.d/php7.2-fpm reload
echo "В конфигурацию "пулов" PHP-FPM успешно внесены настройки для группы сайтов \"${GROUP}\"." | tee -a "${LOG}"
else
echo "На этапе внесения изменений в файл конфигурации PHP-FPM \"${PHPCONF}/fpm/pool.d/${GROUP}.conf\" случился сбой. Операция добавления группы сайтов \"${GROUP}\" прервана." | tee -a "${LOG}"
exit 1
fi
else
echo "Обнаруженный имеющийся файл конфигурации PHP-FPM \"${PHPCONF}/fpm/pool.d/${GROUP}.conf\" оставлен без изменений." | tee -a "${LOG}"
fi
# Проверяем наличие настроек "Supervisor + Memcached" для "группы сайтов" и добавляем их при необходимости
if [ ! -f /etc/supervisor/conf.d/memcached-${GROUP}.conf ] ; then
mkdir -p /etc/supervisor/conf.d
source "$(dirname ${0})/memcached.sh.snippet"
[ "${?}" -ne "0" ] && { echo "Template file Supervisor-Memcached is missing or wrong! Операция добавления группы сайтов \"${GROUP}\" прервана." | tee -a "${LOG}"; exit 1; }
# Применяем изменения конфигурации
supervisorctl reread > /dev/null 2>&1 && supervisorctl update > /dev/null 2>&1
if [ "${?}" -eq "0" ] ; then
echo "В конфигурацию \"Supervisor-Memcached\" успешно внесены настройки для группы сайтов \"${GROUP}\"." | tee -a "${LOG}"
else
echo "На этапе внесения изменений в файл конфигурации \"/etc/supervisor/conf.d/memcached-${GROUP}.conf\" для \"Supervisor-Memcached\" случился сбой. Операция добавления группы сайтов \"${GROUP}\" прервана." | tee -a "${LOG}"
exit 1
fi
else
echo "Обнаруженный имеющийся файл конфигурации Supervisor-Memcached \"/etc/supervisor/conf.d/memcached-${GROUP}.conf\" оставлен без изменений." | tee -a "${LOG}"
fi
# Если поддерживается экспорт "кронтабов" посредством "inCron", то добавляем в отслеживание новую таблицу
if [ -d "/etc/incron.d" ] ; then
touch "/etc/incron.d/web-crontab-export"
if [[ "$(grep -i -c -e "${WEBROOT}/${GROUP}/conf\b" "/etc/incron.d/web-crontab-export")" -eq "0" ]] ; then
source "$(dirname ${0})/incron.sh.snippet" > /dev/null 2>&1
[ "${?}" -ne "0" ] && { echo "Template file inCron is missing or wrong!" | tee -a "${LOG}"; exit 1; }
# Создаём файл "кронтаба" в файловой структуре "группы сайтов"
crontab -u www-${GROUP} -l > ${WEBROOT}/${GROUP}/conf/crontab 2> /dev/null
chown www-${GROUP}:www-${GROUP} ${WEBROOT}/${GROUP}/conf/crontab
chmod g+w ${WEBROOT}/${GROUP}/conf/crontab
chmod o-rwx ${WEBROOT}/${GROUP}/conf/crontab
echo "В конфигурационный файл inCron внесены настройки автоматического экспорта \"${WEBROOT}/${GROUP}/conf/crontab\" для группы сайтов \"${GROUP}\"." | tee -a "${LOG}"
else
echo "Обнаруженная имеющаяся конфигурация "inCron" оставлена без изменений." | tee -a "${LOG}"
fi
fi
# Разрешаем web-серверу переключаться в контекст пользователя, от имени которого будут осуществляться операции с Git-репозитариями
touch /etc/sudoers.d/web-git-deploy-users
if [[ $(grep -i -c -e "^www-data\b\s.*git-${GROUP}:www-${GROUP}.*NOPASSWD:.*/usr/bin/git\b" "/etc/sudoers.d/web-git-deploy-users") -eq "0" ]] ; then
# В профилактических целях корректируем ограничения доступа к файлу конфигурации
chown root:root /etc/sudoers.d/web-git-deploy-users
chmod go-rwx /etc/sudoers.d/web-git-deploy-users
# Вносим добавлением к возможно уже имеющимся разрешительное правило
echo "www-data ALL=(git-${GROUP}:www-${GROUP}) NOPASSWD: /usr/bin/git" >> /etc/sudoers.d/web-git-deploy-users
# Тестируем конфигурацию
visudo -cf /etc/sudoers.d/web-git-deploy-users
if [ "${?}" -eq "0" ] ; then
echo "В конфигурационный файл SUDO успешно внесены разрешительные правила \"автоматизации деплоя\" для группы сайтов \"${GROUP}\"." | tee -a "${LOG}"
else
echo "На этапе внесения изменений в файл конфигурации SUDO \"/etc/sudoers.d/web-git-deploy-users\" случился сбой. Операция добавления группы сайтов \"${GROUP}\" прервана." | tee -a "${LOG}"; exit 1;
fi
else
echo "Обнаруженная имеющаяся конфигурация \"автоматизации деплоя\" в SUDO оставлена без изменений." | tee -a "${LOG}"
fi
exit ${?}
Используемый в скрипте создания "сводной площадки" шаблон блока конфигурации для SSHd-сервера:
# vi /usr/local/etc/webman/sftp.sh.snippet
#!/bin/bash
# This file contains the code snippet for the shell Bash v.4 (Bourne again shell).
# Файл содержит фрагмент кода для командного интерпретатора Bash v.4 (Bourne again shell).
cat << EOF >> /etc/ssh/sshd_config
# Запираем пользователей "группы сайтов" в пределах директории их web-ресурсов
Match Group www-${GROUP} User *
AllowTcpForwarding no
ChrootDirectory ${WEBROOT}/${GROUP}
ForceCommand internal-sftp -u 0007
EOF
# This file contains the code snippet for the shell Bash v.4 (Bourne again shell).
# Файл содержит фрагмент кода для командного интерпретатора Bash v.4 (Bourne again shell).
cat << EOF >> /etc/ssh/sshd_config
# Запираем пользователей "группы сайтов" в пределах директории их web-ресурсов
Match Group www-${GROUP} User *
AllowTcpForwarding no
ChrootDirectory ${WEBROOT}/${GROUP}
ForceCommand internal-sftp -u 0007
EOF
Используемый в скрипте создания "сводной площадки" шаблон блока конфигурации для сервиса PHP-FPM:
# vi /usr/local/etc/webman/php-fpm.sh.snippet
#!/bin/bash
# This file contains the code snippet for the shell Bash v.4 (Bourne again shell).
# Файл содержит фрагмент кода для командного интерпретатора Bash v.4 (Bourne again shell).
cat << EOF > ${PHPCONF}/fpm/pool.d/${GROUP}.conf
; Блок описания отдельного инстанса PHP-FPM
[${GROUP}]
; Указываем запускать инстанс от имени основного пользователя - владельца сайта
user = www-${GROUP}
group = www-${GROUP}
; Задаём точку приёма FCGI-запросов от шлюза (Nginx в нашем случае)
listen = /var/run/php/fpm-${GROUP}.sock
; Явно задаём владельца точки входа FCGI-запросов и разрешения для доступа к ней web-сервера
listen.owner = www-data
listen.group = www-data
listen.mode = 0600
; Режим запуска инстанса
pm = dynamic
pm.start_servers = 20
pm.max_children = 512
pm.min_spare_servers = 5
pm.max_spare_servers = 20
pm.max_requests = 4096
; Очень полезный журнал запросов, блокирующих ресурсы среднестатистически длительное время
slowlog = ${WEBROOT}/${GROUP}/log/php-fcgi.slow.log
; Порог реакции на время (три секунды) исполнения PHP-скрипта, после которого событие запишется в журнал "slowlog"
request_slowlog_timeout = 3s
; Блок переопределения в среде исполнения PHP-FPM глобальных параметров PHP-интерпретатора
php_admin_value[sys_temp_dir] = ${WEBROOT}/${GROUP}/tmp
php_admin_value[post_max_size] = 128M
php_admin_value[upload_max_filesize] = 128M
php_admin_flag[allow_url_fopen] = On
EOF
# This file contains the code snippet for the shell Bash v.4 (Bourne again shell).
# Файл содержит фрагмент кода для командного интерпретатора Bash v.4 (Bourne again shell).
cat << EOF > ${PHPCONF}/fpm/pool.d/${GROUP}.conf
; Блок описания отдельного инстанса PHP-FPM
[${GROUP}]
; Указываем запускать инстанс от имени основного пользователя - владельца сайта
user = www-${GROUP}
group = www-${GROUP}
; Задаём точку приёма FCGI-запросов от шлюза (Nginx в нашем случае)
listen = /var/run/php/fpm-${GROUP}.sock
; Явно задаём владельца точки входа FCGI-запросов и разрешения для доступа к ней web-сервера
listen.owner = www-data
listen.group = www-data
listen.mode = 0600
; Режим запуска инстанса
pm = dynamic
pm.start_servers = 20
pm.max_children = 512
pm.min_spare_servers = 5
pm.max_spare_servers = 20
pm.max_requests = 4096
; Очень полезный журнал запросов, блокирующих ресурсы среднестатистически длительное время
slowlog = ${WEBROOT}/${GROUP}/log/php-fcgi.slow.log
; Порог реакции на время (три секунды) исполнения PHP-скрипта, после которого событие запишется в журнал "slowlog"
request_slowlog_timeout = 3s
; Блок переопределения в среде исполнения PHP-FPM глобальных параметров PHP-интерпретатора
php_admin_value[sys_temp_dir] = ${WEBROOT}/${GROUP}/tmp
php_admin_value[post_max_size] = 128M
php_admin_value[upload_max_filesize] = 128M
php_admin_flag[allow_url_fopen] = On
EOF
Используемый в скрипте создания "сводной площадки" шаблон блока конфигурации для комбинации сервисов "Supervisord" и "Memcached":
# vi /usr/local/etc/webman/memcached.sh.snippet
#!/bin/bash
# This file contains the code snippet for the shell Bash v.4 (Bourne again shell).
# Файл содержит фрагмент кода для командного интерпретатора Bash v.4 (Bourne again shell).
cat << EOF > /etc/supervisor/conf.d/memcached-${GROUP}.conf
# Блок описания отдельного инстанса Supervisor-Memcached
[program:memcached-${GROUP}]
user=www-${GROUP}
command=/usr/bin/memcached -m 128 -c 1024 -t 4 -s /var/run/memcached/memcached-${GROUP}.sock -a 770 -v
stdout_logfile=/var/log/supervisor/memcached-${GROUP}.log
redirect_stderr=true
autostart=true
autorestart=true
stopsignal=KILL
numprocs=1
EOF
# This file contains the code snippet for the shell Bash v.4 (Bourne again shell).
# Файл содержит фрагмент кода для командного интерпретатора Bash v.4 (Bourne again shell).
cat << EOF > /etc/supervisor/conf.d/memcached-${GROUP}.conf
# Блок описания отдельного инстанса Supervisor-Memcached
[program:memcached-${GROUP}]
user=www-${GROUP}
command=/usr/bin/memcached -m 128 -c 1024 -t 4 -s /var/run/memcached/memcached-${GROUP}.sock -a 770 -v
stdout_logfile=/var/log/supervisor/memcached-${GROUP}.log
redirect_stderr=true
autostart=true
autorestart=true
stopsignal=KILL
numprocs=1
EOF
Используемый в скрипте создания "сводной площадки" шаблон блока конфигурации для комбинации сервисов "inCron" и "Crontab":
# vi /usr/local/etc/webman/incron.sh.snippet
#!/bin/bash
# This file contains the code snippet for the shell Bash v.4 (Bourne again shell).
# Файл содержит фрагмент кода для командного интерпретатора Bash v.4 (Bourne again shell).
cat << EOF >> /etc/incron.d/web-crontab-export
${WEBROOT}/${GROUP}/conf IN_CREATE /etc/init.d/incron reload &
${WEBROOT}/${GROUP}/conf/crontab IN_MODIFY,IN_CLOSE_WRITE,IN_MOVE crontab -u www-${GROUP} \$@
EOF
# This file contains the code snippet for the shell Bash v.4 (Bourne again shell).
# Файл содержит фрагмент кода для командного интерпретатора Bash v.4 (Bourne again shell).
cat << EOF >> /etc/incron.d/web-crontab-export
${WEBROOT}/${GROUP}/conf IN_CREATE /etc/init.d/incron reload &
${WEBROOT}/${GROUP}/conf/crontab IN_MODIFY,IN_CLOSE_WRITE,IN_MOVE crontab -u www-${GROUP} \$@
EOF
Скрипт создания площадки web-сайта.
# vi /usr/local/etc/webman/addwebsite.sh && chmod ug+x /usr/local/etc/webman/addwebsite.sh
#!/bin/bash
NGINXCONF="/etc/nginx"
WEBROOT="/var/www"
GROUP=${1}
SITE=${2}
LOG="$(dirname ${0})/add.log"
DATE=$(date +"%Y-%m-%d %H:%M:%S")
# Проверяем наличие ожидаемых приложений и утилит
[ -x "$(command -v nginx)" ] && [ -x "$(command -v setfacl)" ] || { echo "Не обнаружен необходимый для работы набор приложений и утилит. Операция создания сайта прервана."; exit 1; }
# Проверяем корректность вводимых данных и выводим подсказку в случае необходимости
# (если сайт уже имеется - ничего страшного - профилактически перезаписываем структуру директорий)
#
[ ! "${1}" ] && { echo "Ожидаемый формат: ${0} \"group\" \"domain.name\""; echo "Не указано название \"группы сайтов\". Операция создания сайта прервана."; exit 1; }
[ ! -d "${WEBROOT}/${GROUP}" ] && { echo "Указанная \"группа сайтов\" отсутствует. Операция создания сайта прервана."; exit 1; }
#
[ ! "${2}" ] && { echo "Ожидаемый формат: ${0} \"${GROUP}\" \"domain.name\""; echo "Не указано имя сайта. Операция создания сайта прервана."; exit 1; }
[ $(echo "${SITE}" | grep -i -c -e "^www\.") -ne "0" ] && { echo "Имя сайта необходимо указывать без префикса \"www.\". Операция создания сайта прервана."; exit 1; }
# Проверяем наличие сайта в других "группах сайтов" и прерываем работу при обнаружении такового в другом месте
[ $(find ${WEBROOT} -maxdepth 2 -type d -name "${SITE}" ! -path "${WEBROOT}/${GROUP}*" | wc -l) -ne "0" ] && { echo "Одноимённый сайт обнаружен в другой \"группе сайтов\". Операция создания сайта прервана."; exit 1; }
echo >> "${LOG}"
echo "${DATE}:" >> "${LOG}"
# Создаём файловую структуру для сайта
mkdir -p ${WEBROOT}/${GROUP}/${SITE}
mkdir -p ${WEBROOT}/${GROUP}/${SITE}/nodejs
mkdir -p ${WEBROOT}/${GROUP}/${SITE}/www
# Запрещаем изменение корневой структуры ресурса
setfacl --no-mask --modify user::rX,group::rX,other:--- ${WEBROOT}/${GROUP}/${SITE}
# Явно разрешаем доступ на чтение web-серверу (к существующим и создаваемым в будущем ресурсам)
setfacl --no-mask --modify user:www-data:X ${WEBROOT}/${GROUP}/${SITE}
setfacl --recursive --no-mask --modify user:www-data:rX ${WEBROOT}/${GROUP}/${SITE}/www
setfacl --recursive --no-mask --modify default:user:www-data:rX ${WEBROOT}/${GROUP}/${SITE}/www
# Создаём страницу-заглушку
if [ $(find "${WEBROOT}/${GROUP}/${SITE}/www" -maxdepth 1 -type f -iname "index.*" | wc -l) -eq 0 ] ; then
echo "${SITE}" > "${WEBROOT}/${GROUP}/${SITE}/www/index.html"
fi
# Переводим файловую структуру сайта во владение опорного пользователя
chown -R www-${GROUP}:www-${GROUP} ${WEBROOT}/${GROUP}/${SITE}
chmod -R ug+rw ${WEBROOT}/${GROUP}/${SITE}
setfacl --recursive --modify user::rwX,group::rwX,other:--- ${WEBROOT}/${GROUP}/${SITE}
# Проверяем успешность создания опорной файловой структуры
if [ -d "${WEBROOT}/${GROUP}/${SITE}/www" ] ; then
echo "Успешно создана опорная файловая структура для сайта \"${SITE}\"." | tee -a "${LOG}"
else
echo "В процессе создания файловой структуры случился сбой. Операция добавления сайта \"${SITE}\" прервана." | tee -a "${LOG}"
exit 1
fi
# Проверяем наличие настроек Nginx для сайта и добавляем их при необходимости
if [ ! -f ${NGINXCONF}/sites-available/${SITE}.conf ] ; then
mkdir -p ${NGINXCONF}/sites-available
mkdir -p ${NGINXCONF}/sites-enabled
source "$(dirname ${0})/nginx.sh.snippet"
[ "${?}" -ne "0" ] && { echo "Template file Nginx is missing or wrong! Операция добавления сайта прервана." | tee -a "${LOG}"; exit 1; }
ln -s ${NGINXCONF}/sites-available/${SITE}.conf ${NGINXCONF}/sites-enabled/${SITE}.conf
# Тестируем конфигурацию и перезапускаем сервис
nginx -t
if [ "${?}" -eq "0" ] ; then
/etc/init.d/nginx reload
echo "Успешно внесены изменения в конфигурацию сайтов Nginx." | tee -a "${LOG}"
else
echo "На этапе внесения изменений в файл конфигурации Nginx \"${NGINXCONF}/sites-available/${SITE}.conf\" случился сбой. Операция добавления сайта прервана." | tee -a "${LOG}"
exit 1
fi
else
echo "Обнаружен имеющийся файл конфигурации Nginx \"${NGINXCONF}/sites-available/${SITE}.conf\" - оставлен без изменений." | tee -a "${LOG}"
fi
# Профилактически обновляем установки разрешений на доступ к файлам журналов событий
chown www-${GROUP}:www-${GROUP} /var/www/${GROUP}/log/*
chmod g+rw /var/www/${GROUP}/log/*
# Для полноты картины тестируем доступность доменного имени создаваемого сайта
IP=$(dig +short "${SITE}")
if [ "${IP}" ] ; then
if [ $(ip address | grep -i -c "${IP}") -eq "0" ] ; then
echo "IP-адрес сопоставленный с доменным именем создаваемого сайта не ведёт на этот сервер!" | tee -a "${LOG}"
fi
else
echo "Доменному имени создаваемого сайта не сопоставлен IP-адрес!" | tee -a "${LOG}"
fi
exit ${?}
NGINXCONF="/etc/nginx"
WEBROOT="/var/www"
GROUP=${1}
SITE=${2}
LOG="$(dirname ${0})/add.log"
DATE=$(date +"%Y-%m-%d %H:%M:%S")
# Проверяем наличие ожидаемых приложений и утилит
[ -x "$(command -v nginx)" ] && [ -x "$(command -v setfacl)" ] || { echo "Не обнаружен необходимый для работы набор приложений и утилит. Операция создания сайта прервана."; exit 1; }
# Проверяем корректность вводимых данных и выводим подсказку в случае необходимости
# (если сайт уже имеется - ничего страшного - профилактически перезаписываем структуру директорий)
#
[ ! "${1}" ] && { echo "Ожидаемый формат: ${0} \"group\" \"domain.name\""; echo "Не указано название \"группы сайтов\". Операция создания сайта прервана."; exit 1; }
[ ! -d "${WEBROOT}/${GROUP}" ] && { echo "Указанная \"группа сайтов\" отсутствует. Операция создания сайта прервана."; exit 1; }
#
[ ! "${2}" ] && { echo "Ожидаемый формат: ${0} \"${GROUP}\" \"domain.name\""; echo "Не указано имя сайта. Операция создания сайта прервана."; exit 1; }
[ $(echo "${SITE}" | grep -i -c -e "^www\.") -ne "0" ] && { echo "Имя сайта необходимо указывать без префикса \"www.\". Операция создания сайта прервана."; exit 1; }
# Проверяем наличие сайта в других "группах сайтов" и прерываем работу при обнаружении такового в другом месте
[ $(find ${WEBROOT} -maxdepth 2 -type d -name "${SITE}" ! -path "${WEBROOT}/${GROUP}*" | wc -l) -ne "0" ] && { echo "Одноимённый сайт обнаружен в другой \"группе сайтов\". Операция создания сайта прервана."; exit 1; }
echo >> "${LOG}"
echo "${DATE}:" >> "${LOG}"
# Создаём файловую структуру для сайта
mkdir -p ${WEBROOT}/${GROUP}/${SITE}
mkdir -p ${WEBROOT}/${GROUP}/${SITE}/nodejs
mkdir -p ${WEBROOT}/${GROUP}/${SITE}/www
# Запрещаем изменение корневой структуры ресурса
setfacl --no-mask --modify user::rX,group::rX,other:--- ${WEBROOT}/${GROUP}/${SITE}
# Явно разрешаем доступ на чтение web-серверу (к существующим и создаваемым в будущем ресурсам)
setfacl --no-mask --modify user:www-data:X ${WEBROOT}/${GROUP}/${SITE}
setfacl --recursive --no-mask --modify user:www-data:rX ${WEBROOT}/${GROUP}/${SITE}/www
setfacl --recursive --no-mask --modify default:user:www-data:rX ${WEBROOT}/${GROUP}/${SITE}/www
# Создаём страницу-заглушку
if [ $(find "${WEBROOT}/${GROUP}/${SITE}/www" -maxdepth 1 -type f -iname "index.*" | wc -l) -eq 0 ] ; then
echo "${SITE}" > "${WEBROOT}/${GROUP}/${SITE}/www/index.html"
fi
# Переводим файловую структуру сайта во владение опорного пользователя
chown -R www-${GROUP}:www-${GROUP} ${WEBROOT}/${GROUP}/${SITE}
chmod -R ug+rw ${WEBROOT}/${GROUP}/${SITE}
setfacl --recursive --modify user::rwX,group::rwX,other:--- ${WEBROOT}/${GROUP}/${SITE}
# Проверяем успешность создания опорной файловой структуры
if [ -d "${WEBROOT}/${GROUP}/${SITE}/www" ] ; then
echo "Успешно создана опорная файловая структура для сайта \"${SITE}\"." | tee -a "${LOG}"
else
echo "В процессе создания файловой структуры случился сбой. Операция добавления сайта \"${SITE}\" прервана." | tee -a "${LOG}"
exit 1
fi
# Проверяем наличие настроек Nginx для сайта и добавляем их при необходимости
if [ ! -f ${NGINXCONF}/sites-available/${SITE}.conf ] ; then
mkdir -p ${NGINXCONF}/sites-available
mkdir -p ${NGINXCONF}/sites-enabled
source "$(dirname ${0})/nginx.sh.snippet"
[ "${?}" -ne "0" ] && { echo "Template file Nginx is missing or wrong! Операция добавления сайта прервана." | tee -a "${LOG}"; exit 1; }
ln -s ${NGINXCONF}/sites-available/${SITE}.conf ${NGINXCONF}/sites-enabled/${SITE}.conf
# Тестируем конфигурацию и перезапускаем сервис
nginx -t
if [ "${?}" -eq "0" ] ; then
/etc/init.d/nginx reload
echo "Успешно внесены изменения в конфигурацию сайтов Nginx." | tee -a "${LOG}"
else
echo "На этапе внесения изменений в файл конфигурации Nginx \"${NGINXCONF}/sites-available/${SITE}.conf\" случился сбой. Операция добавления сайта прервана." | tee -a "${LOG}"
exit 1
fi
else
echo "Обнаружен имеющийся файл конфигурации Nginx \"${NGINXCONF}/sites-available/${SITE}.conf\" - оставлен без изменений." | tee -a "${LOG}"
fi
# Профилактически обновляем установки разрешений на доступ к файлам журналов событий
chown www-${GROUP}:www-${GROUP} /var/www/${GROUP}/log/*
chmod g+rw /var/www/${GROUP}/log/*
# Для полноты картины тестируем доступность доменного имени создаваемого сайта
IP=$(dig +short "${SITE}")
if [ "${IP}" ] ; then
if [ $(ip address | grep -i -c "${IP}") -eq "0" ] ; then
echo "IP-адрес сопоставленный с доменным именем создаваемого сайта не ведёт на этот сервер!" | tee -a "${LOG}"
fi
else
echo "Доменному имени создаваемого сайта не сопоставлен IP-адрес!" | tee -a "${LOG}"
fi
exit ${?}
Используемый в скрипте создания площадки web-сайта шаблон блока конфигурации для web-сервера "Nginx":
# vi /usr/local/etc/webman/nginx.sh.snippet
#!/bin/bash
# This file contains the code snippet for the shell Bash v.4 (Bourne again shell).
# Файл содержит фрагмент кода для командного интерпретатора Bash v.4 (Bourne again shell).
cat << EOF > ${NGINXCONF}/sites-available/${SITE}.conf
# Блок отлова HTTP-обращений и перенаправления их на HTTPS
server {
listen 80;
server_name www.${SITE} ${SITE};
location / {
rewrite ^ https://${SITE}\$request_uri permanent;
}
}
# Блок отлова обращений по префиксу "www." и перенаправления их на "Canonical URL"
server {
listen 443 ssl http2;
server_name www.${SITE};
include /etc/nginx/ssl_wildcard.conf;
location / {
rewrite ^ https://${SITE}\$request_uri permanent;
}
}
# Описание параметров web-сайта как такового
server {
# Указываем сетевые порты и протоколы для приёма клиентских подключений
listen 443 ssl http2;
# Перечисляем обслуживаемые FQDN сайта
server_name ${SITE};
access_log /var/www/${GROUP}/log/nginx-${SITE}-access.log;
error_log /var/www/${GROUP}/log/nginx-${SITE}-error.log;
# Принудительно переводим сайт на работу только через SSL
ssl on;
include /etc/nginx/ssl_wildcard.conf;
# Выключаем невостребованную перекодировку контента
charset off;
# Задаём переменную с многократно используемым параметром PHP-FPM
set \$php_pass unix:/var/run/php/php-fpm-${GROUP}.sock;
root /var/www/${GROUP}/${SITE}/www;
index index.html index.htm index.phtml index.php;
# Подставляем свои страницы обработки ошибок
error_page 403 /403.html;
error_page 404 /404.html;
error_page 500 502 503 504 /50x.html;
# ACL фильтрующий клиентские IP
#allow 10.0.0.0/8;
#allow 172.16.0.0/12;
#allow 192.168.0.0/16;
#deny all;
# Глобальный обработчик запросов
location / {
# Обработчик событий отсутствия запрашиваемого файла
try_files \$uri \$uri/ =404;
}
# Блокируем доступ к типовым "закрытым" ресурсам
location ~* (/\.ht|/\.hg|/\.git|/\.svn|/\.subversion|/\.inc|/\.sys|/\.local|/\.env|/\.enabled|/\.config|/\.profile) {
deny all;
log_not_found off;
access_log off;
}
# Блокируем доступ к ресурсам, которые часто забывают спрятать
location ~* (/conf|/cnf|/inc|/log/|/tmp/|/temp/|/runtime|/back|/bkp|/bak|/old|/test|/protected|/base|/database|/exchange|/phpshell|/shell|/cli|/bin|\.sql|\.py|\.perl|\.tpl|\.sh|\.bash|\.dist|\.orig|\.back|\.bak|\.conf|phpinfo.php) {
deny all;
log_not_found off;
}
# Блокируем доступ к классически неправильно и опасно именованным файлам (вроде ".php.1")
location ~* \.(phtml|php)(?!(\?|\/|\$)) {
deny all;
log_not_found off;
}
# Обработчик прямых обращений к PHP-скриптам
location ~* \.(phtml|php)\$ {
try_files \$uri =404;
include /etc/nginx/fastcgi_params;
fastcgi_pass \$php_pass;
fastcgi_param SCRIPT_FILENAME \$document_root\$fastcgi_script_name;
}
# Не реагируем на неинтересные события загрузок
location = /(favicon.ico|robots.txt|sitemap.xml) {
log_not_found off;
access_log off;
}
# Напрямую отдаём "статические" данные, предлагая браузеру сохранить их в своём "кеше", и не фиксируем эти события
location ~* ^.+\.(jpg|jpeg|gif|png|svg|js|css|mp3|ogg|mpe?g|avi|zip|gz|bz2?|rar|swf|xml|txt)\$ {
try_files \$uri =404;
expires 30d;
access_log off;
}
}
EOF
# This file contains the code snippet for the shell Bash v.4 (Bourne again shell).
# Файл содержит фрагмент кода для командного интерпретатора Bash v.4 (Bourne again shell).
cat << EOF > ${NGINXCONF}/sites-available/${SITE}.conf
# Блок отлова HTTP-обращений и перенаправления их на HTTPS
server {
listen 80;
server_name www.${SITE} ${SITE};
location / {
rewrite ^ https://${SITE}\$request_uri permanent;
}
}
# Блок отлова обращений по префиксу "www." и перенаправления их на "Canonical URL"
server {
listen 443 ssl http2;
server_name www.${SITE};
include /etc/nginx/ssl_wildcard.conf;
location / {
rewrite ^ https://${SITE}\$request_uri permanent;
}
}
# Описание параметров web-сайта как такового
server {
# Указываем сетевые порты и протоколы для приёма клиентских подключений
listen 443 ssl http2;
# Перечисляем обслуживаемые FQDN сайта
server_name ${SITE};
access_log /var/www/${GROUP}/log/nginx-${SITE}-access.log;
error_log /var/www/${GROUP}/log/nginx-${SITE}-error.log;
# Принудительно переводим сайт на работу только через SSL
ssl on;
include /etc/nginx/ssl_wildcard.conf;
# Выключаем невостребованную перекодировку контента
charset off;
# Задаём переменную с многократно используемым параметром PHP-FPM
set \$php_pass unix:/var/run/php/php-fpm-${GROUP}.sock;
root /var/www/${GROUP}/${SITE}/www;
index index.html index.htm index.phtml index.php;
# Подставляем свои страницы обработки ошибок
error_page 403 /403.html;
error_page 404 /404.html;
error_page 500 502 503 504 /50x.html;
# ACL фильтрующий клиентские IP
#allow 10.0.0.0/8;
#allow 172.16.0.0/12;
#allow 192.168.0.0/16;
#deny all;
# Глобальный обработчик запросов
location / {
# Обработчик событий отсутствия запрашиваемого файла
try_files \$uri \$uri/ =404;
}
# Блокируем доступ к типовым "закрытым" ресурсам
location ~* (/\.ht|/\.hg|/\.git|/\.svn|/\.subversion|/\.inc|/\.sys|/\.local|/\.env|/\.enabled|/\.config|/\.profile) {
deny all;
log_not_found off;
access_log off;
}
# Блокируем доступ к ресурсам, которые часто забывают спрятать
location ~* (/conf|/cnf|/inc|/log/|/tmp/|/temp/|/runtime|/back|/bkp|/bak|/old|/test|/protected|/base|/database|/exchange|/phpshell|/shell|/cli|/bin|\.sql|\.py|\.perl|\.tpl|\.sh|\.bash|\.dist|\.orig|\.back|\.bak|\.conf|phpinfo.php) {
deny all;
log_not_found off;
}
# Блокируем доступ к классически неправильно и опасно именованным файлам (вроде ".php.1")
location ~* \.(phtml|php)(?!(\?|\/|\$)) {
deny all;
log_not_found off;
}
# Обработчик прямых обращений к PHP-скриптам
location ~* \.(phtml|php)\$ {
try_files \$uri =404;
include /etc/nginx/fastcgi_params;
fastcgi_pass \$php_pass;
fastcgi_param SCRIPT_FILENAME \$document_root\$fastcgi_script_name;
}
# Не реагируем на неинтересные события загрузок
location = /(favicon.ico|robots.txt|sitemap.xml) {
log_not_found off;
access_log off;
}
# Напрямую отдаём "статические" данные, предлагая браузеру сохранить их в своём "кеше", и не фиксируем эти события
location ~* ^.+\.(jpg|jpeg|gif|png|svg|js|css|mp3|ogg|mpe?g|avi|zip|gz|bz2?|rar|swf|xml|txt)\$ {
try_files \$uri =404;
expires 30d;
access_log off;
}
}
EOF
Скрипт создания "базы данных" web-сайта в СУБД "MySQL".
# vi /usr/local/etc/webman/addwebmysql.sh && chmod ug+x /usr/local/etc/webman/addwebmysql.sh
#!/bin/bash
WEBROOT="/var/www"
GROUP=${1}
SITE=${2}
MSITE=$(echo ${SITE} | sed 's/\./_/g' | sed 's/\-/_/g')
MPASSWORD=$(/usr/bin/pwgen --capitalize --numerals --secure 12 1)
LOG="$(dirname ${0})/add.log"
DATE=$(date +"%Y-%m-%d %H:%M:%S")
# Проверяем наличие ожидаемых приложений и утилит
[ -x "$(command -v mysql)" ] || { echo "Не обнаружен необходимый для работы набор приложений и утилит. Операция создания "баз данных" MySQL для сайта прервана."; exit 1; }
# Проверяем корректность вводимых данных и выводим подсказку в случае необходимости
[ ! "${1}" ] && { echo "Ожидаемый формат: ${0} \"group\" \"domain.name\""; echo "Не указано название \"группы сайтов\". Операция создания БД MySQL для сайта прервана"; exit 1; }
[ ! -d "${WEBROOT}/${GROUP}" ] && { echo "Указанная \"группа сайтов\" отсутствует. Операция создания БД MySQL для сайта прервана."; exit 1; }
#
[ ! "${2}" ] && { echo "Ожидаемый формат: ${0} \"${GROUP}\" \"domain.name\""; echo "Не указано имя сайта. Операция создания БД MySQL для сайта прервана"; exit 1; }
[ $(find ${WEBROOT}/${GROUP}/* -maxdepth 1 -type d -name "${SITE}" | wc -l) -eq "0" ] && { echo "Указанный сайт отсутствует. Операция создания БД MySQL для сайта прервана."; exit 1; }
echo >> "${LOG}"
echo "${DATE}:" >> "${LOG}"
# Проверяем наличие БД MySQL для сайта и добавляем таковую только при необходимости
if [ $(mysql -h localhost -u root -e "show databases" | grep -i -c -e "^${MSITE}$") -eq "0" ] ; then
# Создаём БД
mysql -h localhost -u root -e "create database if not exists ${MSITE}"
if [ "${?}" -eq "0" ] ; then
echo "Успешно создана БД MySQL \"${MSITE}\" для сайта \"${SITE}\"." | tee -a "${LOG}"
else
echo "На этапе создания БД MySQL \"${MSITE}\" случился сбой. Операция создания базы данных сайта \"${SITE}\" прервана."
exit 1
fi
else
echo "Создаваемая БД MySQL \"${MSITE}\" уже существует."
fi
# Проверяем наличие пользователя БД MySQL для сайта и добавляем такового только при необходимости
if [ $(mysql -h localhost -u root -e "select user from mysql.user" | grep -i -c -e "^${MSITE}$") -eq "0" ] ; then
# Добавляем пользователя БД сайта
mysql -h localhost -u root -e "grant alter,create,create temporary tables,delete,drop,index,insert,lock tables,references,select,update on ${MSITE}.* to '${MSITE}'@'localhost' identified by '${MPASSWORD}'"
if [ "${?}" -eq "0" ] ; then
# Для скорейшего принятия СУБД новых параметров пользовательских привилегий отдаём команду явно их обновить
mysql -h localhost -u root -e "flush privileges"
# Распечатываем сведения о созданной БД
echo
echo "Успешно создан пользователь БД MySQL для сайта \"${SITE}\":" | tee -a "${LOG}"
echo " MySQL Server: localhost" | tee -a "${LOG}"
echo " MySQL DB: ${MSITE}" | tee -a "${LOG}"
echo " MySQL User: ${MSITE}" | tee -a "${LOG}"
echo " MySQL Password: ${MPASSWORD}" | tee -a "${LOG}"
echo
else
echo "На этапе создания пользователя \"${MSITE}\" БД MySQL для сайта \"${SITE}\" случился сбой. Операция создания пользователя базы данных сайта прервана."
exit 1
fi
else
echo "Создаваемый пользователь \"${MSITE}\" БД MySQL уже существует."
fi
exit ${?}
WEBROOT="/var/www"
GROUP=${1}
SITE=${2}
MSITE=$(echo ${SITE} | sed 's/\./_/g' | sed 's/\-/_/g')
MPASSWORD=$(/usr/bin/pwgen --capitalize --numerals --secure 12 1)
LOG="$(dirname ${0})/add.log"
DATE=$(date +"%Y-%m-%d %H:%M:%S")
# Проверяем наличие ожидаемых приложений и утилит
[ -x "$(command -v mysql)" ] || { echo "Не обнаружен необходимый для работы набор приложений и утилит. Операция создания "баз данных" MySQL для сайта прервана."; exit 1; }
# Проверяем корректность вводимых данных и выводим подсказку в случае необходимости
[ ! "${1}" ] && { echo "Ожидаемый формат: ${0} \"group\" \"domain.name\""; echo "Не указано название \"группы сайтов\". Операция создания БД MySQL для сайта прервана"; exit 1; }
[ ! -d "${WEBROOT}/${GROUP}" ] && { echo "Указанная \"группа сайтов\" отсутствует. Операция создания БД MySQL для сайта прервана."; exit 1; }
#
[ ! "${2}" ] && { echo "Ожидаемый формат: ${0} \"${GROUP}\" \"domain.name\""; echo "Не указано имя сайта. Операция создания БД MySQL для сайта прервана"; exit 1; }
[ $(find ${WEBROOT}/${GROUP}/* -maxdepth 1 -type d -name "${SITE}" | wc -l) -eq "0" ] && { echo "Указанный сайт отсутствует. Операция создания БД MySQL для сайта прервана."; exit 1; }
echo >> "${LOG}"
echo "${DATE}:" >> "${LOG}"
# Проверяем наличие БД MySQL для сайта и добавляем таковую только при необходимости
if [ $(mysql -h localhost -u root -e "show databases" | grep -i -c -e "^${MSITE}$") -eq "0" ] ; then
# Создаём БД
mysql -h localhost -u root -e "create database if not exists ${MSITE}"
if [ "${?}" -eq "0" ] ; then
echo "Успешно создана БД MySQL \"${MSITE}\" для сайта \"${SITE}\"." | tee -a "${LOG}"
else
echo "На этапе создания БД MySQL \"${MSITE}\" случился сбой. Операция создания базы данных сайта \"${SITE}\" прервана."
exit 1
fi
else
echo "Создаваемая БД MySQL \"${MSITE}\" уже существует."
fi
# Проверяем наличие пользователя БД MySQL для сайта и добавляем такового только при необходимости
if [ $(mysql -h localhost -u root -e "select user from mysql.user" | grep -i -c -e "^${MSITE}$") -eq "0" ] ; then
# Добавляем пользователя БД сайта
mysql -h localhost -u root -e "grant alter,create,create temporary tables,delete,drop,index,insert,lock tables,references,select,update on ${MSITE}.* to '${MSITE}'@'localhost' identified by '${MPASSWORD}'"
if [ "${?}" -eq "0" ] ; then
# Для скорейшего принятия СУБД новых параметров пользовательских привилегий отдаём команду явно их обновить
mysql -h localhost -u root -e "flush privileges"
# Распечатываем сведения о созданной БД
echo
echo "Успешно создан пользователь БД MySQL для сайта \"${SITE}\":" | tee -a "${LOG}"
echo " MySQL Server: localhost" | tee -a "${LOG}"
echo " MySQL DB: ${MSITE}" | tee -a "${LOG}"
echo " MySQL User: ${MSITE}" | tee -a "${LOG}"
echo " MySQL Password: ${MPASSWORD}" | tee -a "${LOG}"
echo
else
echo "На этапе создания пользователя \"${MSITE}\" БД MySQL для сайта \"${SITE}\" случился сбой. Операция создания пользователя базы данных сайта прервана."
exit 1
fi
else
echo "Создаваемый пользователь \"${MSITE}\" БД MySQL уже существует."
fi
exit ${?}
Скрипт создания "базы данных" web-сайта в СУБД "MongoDB".
# vi /usr/local/etc/webman/addwebmongodb.sh && chmod ug+x /usr/local/etc/webman/addwebongobd.sh
#!/bin/bash
WEBROOT="/var/www"
GROUP=${1}
SITE=${2}
MSITE=$(echo ${SITE} | sed 's/\./_/g' | sed 's/\-/_/g')
MPASSWORD=$(/usr/bin/pwgen --capitalize --numerals --secure 12 1)
LOG="$(dirname ${0})/add.log"
DATE=$(date +"%Y-%m-%d %H:%M:%S")
# Проверяем наличие ожидаемых приложений и утилит
[ -x "$(command -v mongo)" ] && [ -x "$(command -v pwgen)" ] || { echo "Не обнаружен необходимый для работы набор приложений и утилит. Операция создания "баз данных" MongoDB для сайта прервана."; exit 1; }
# Проверяем корректность вводимых данных и выводим подсказку в случае необходимости
[ ! "${1}" ] && { echo "Ожидаемый формат: ${0} \"group\" \"domain.name\""; echo "Не указано название \"группы сайтов\". Операция создания БД MongoDB для сайта прервана"; exit 1; }
[ ! -d "${WEBROOT}/${GROUP}" ] && { echo "Указанная \"группа сайтов\" отсутствует. Операция создания БД MongoDB для сайта прервана."; exit 1; }
#
[ ! "${2}" ] && { echo "Ожидаемый формат: ${0} \"${GROUP}\" \"domain.name\""; echo "Не указано имя сайта. Операция создания БД MongoDB для сайта прервана"; exit 1; }
[ $(find ${WEBROOT}/${GROUP}/* -maxdepth 1 -type d -name "${SITE}" | wc -l) -eq "0" ] && { echo "Указанный сайт отсутствует. Операция создания БД MongoDB для сайта прервана."; exit 1; }
echo >> "${LOG}"
echo "${DATE}:" >> "${LOG}"
# Регистрируем временный "алиас" для упрощения синтаксиса дальнейших команд
alias mongo='mongo --quiet localhost/admin --username root --password "`cat /root/.mongodb`"'
shopt -s expand_aliases
# Проверяем наличие БД MongoDB для сайта и добавляем таковую только при необходимости
if [ $(mongo --eval "db.adminCommand('listDatabases')" | grep -i "name" | grep -i -c -e "\"${MSITE}\"") -eq "0" ] ; then
# Создаём БД
if [ $(mongo --eval "db=db.getSiblingDB('${MSITE}');db.init.insert({sitename:'${MSITE}'})" | grep -i "nInserted" | grep -i -c "1") -eq "1" ] ; then
echo "Успешно создана БД MongoDB \"${MSITE}\" для сайта \"${SITE}\"." | tee -a "${LOG}"
else
echo "На этапе создания БД MongoDB \"${MSITE}\" случился сбой. Операция создания базы данных сайта \"${SITE}\" прервана."
exit 1
fi
else
echo "Создаваемая БД MongoDB \"${MSITE}\" уже существует."
fi
# Проверяем наличие пользователя БД MongoDB для сайта и добавляем такового только при необходимости
if [ $(mongo --eval "db = db.getSiblingDB('${MSITE}') ; db.getUsers()" | grep -i "user" | grep -i -c -e "\"${MSITE}\"") -eq "0" ] ; then
# Добавляем пользователя БД сайта
if [ $(mongo --eval "db=db.getSiblingDB('${MSITE}');db.createUser({user:'${MSITE}',pwd:'${MPASSWORD}',roles:[{role:'readWrite',db:'${MSITE}'}]})" | grep -i -c "successfully") -eq "1" ] ; then
# Распечатываем сведения о созданной БД
echo
echo "Успешно создан пользователь БД MongoDB для сайта \"${SITE}\":" | tee -a "${LOG}"
echo " MongoDB Server: localhost" | tee -a "${LOG}"
echo " MongoDB DB: ${MSITE}" | tee -a "${LOG}"
echo " MongoDB User: ${MSITE}" | tee -a "${LOG}"
echo " MongoDB Password: ${MPASSWORD}" | tee -a "${LOG}"
echo
else
echo "На этапе создания пользователя \"${MSITE}\" БД MongoDB для сайта \"${SITE}\" случился сбой. Операция создания пользователя базы данных сайта прервана."
exit 1
fi
else
echo "Создаваемый пользователь \"${MSITE}\" БД MongoDB уже существует."
fi
exit ${?}
WEBROOT="/var/www"
GROUP=${1}
SITE=${2}
MSITE=$(echo ${SITE} | sed 's/\./_/g' | sed 's/\-/_/g')
MPASSWORD=$(/usr/bin/pwgen --capitalize --numerals --secure 12 1)
LOG="$(dirname ${0})/add.log"
DATE=$(date +"%Y-%m-%d %H:%M:%S")
# Проверяем наличие ожидаемых приложений и утилит
[ -x "$(command -v mongo)" ] && [ -x "$(command -v pwgen)" ] || { echo "Не обнаружен необходимый для работы набор приложений и утилит. Операция создания "баз данных" MongoDB для сайта прервана."; exit 1; }
# Проверяем корректность вводимых данных и выводим подсказку в случае необходимости
[ ! "${1}" ] && { echo "Ожидаемый формат: ${0} \"group\" \"domain.name\""; echo "Не указано название \"группы сайтов\". Операция создания БД MongoDB для сайта прервана"; exit 1; }
[ ! -d "${WEBROOT}/${GROUP}" ] && { echo "Указанная \"группа сайтов\" отсутствует. Операция создания БД MongoDB для сайта прервана."; exit 1; }
#
[ ! "${2}" ] && { echo "Ожидаемый формат: ${0} \"${GROUP}\" \"domain.name\""; echo "Не указано имя сайта. Операция создания БД MongoDB для сайта прервана"; exit 1; }
[ $(find ${WEBROOT}/${GROUP}/* -maxdepth 1 -type d -name "${SITE}" | wc -l) -eq "0" ] && { echo "Указанный сайт отсутствует. Операция создания БД MongoDB для сайта прервана."; exit 1; }
echo >> "${LOG}"
echo "${DATE}:" >> "${LOG}"
# Регистрируем временный "алиас" для упрощения синтаксиса дальнейших команд
alias mongo='mongo --quiet localhost/admin --username root --password "`cat /root/.mongodb`"'
shopt -s expand_aliases
# Проверяем наличие БД MongoDB для сайта и добавляем таковую только при необходимости
if [ $(mongo --eval "db.adminCommand('listDatabases')" | grep -i "name" | grep -i -c -e "\"${MSITE}\"") -eq "0" ] ; then
# Создаём БД
if [ $(mongo --eval "db=db.getSiblingDB('${MSITE}');db.init.insert({sitename:'${MSITE}'})" | grep -i "nInserted" | grep -i -c "1") -eq "1" ] ; then
echo "Успешно создана БД MongoDB \"${MSITE}\" для сайта \"${SITE}\"." | tee -a "${LOG}"
else
echo "На этапе создания БД MongoDB \"${MSITE}\" случился сбой. Операция создания базы данных сайта \"${SITE}\" прервана."
exit 1
fi
else
echo "Создаваемая БД MongoDB \"${MSITE}\" уже существует."
fi
# Проверяем наличие пользователя БД MongoDB для сайта и добавляем такового только при необходимости
if [ $(mongo --eval "db = db.getSiblingDB('${MSITE}') ; db.getUsers()" | grep -i "user" | grep -i -c -e "\"${MSITE}\"") -eq "0" ] ; then
# Добавляем пользователя БД сайта
if [ $(mongo --eval "db=db.getSiblingDB('${MSITE}');db.createUser({user:'${MSITE}',pwd:'${MPASSWORD}',roles:[{role:'readWrite',db:'${MSITE}'}]})" | grep -i -c "successfully") -eq "1" ] ; then
# Распечатываем сведения о созданной БД
echo
echo "Успешно создан пользователь БД MongoDB для сайта \"${SITE}\":" | tee -a "${LOG}"
echo " MongoDB Server: localhost" | tee -a "${LOG}"
echo " MongoDB DB: ${MSITE}" | tee -a "${LOG}"
echo " MongoDB User: ${MSITE}" | tee -a "${LOG}"
echo " MongoDB Password: ${MPASSWORD}" | tee -a "${LOG}"
echo
else
echo "На этапе создания пользователя \"${MSITE}\" БД MongoDB для сайта \"${SITE}\" случился сбой. Операция создания пользователя базы данных сайта прервана."
exit 1
fi
else
echo "Создаваемый пользователь \"${MSITE}\" БД MongoDB уже существует."
fi
exit ${?}
Скрипт создания аккаунта web-разработчика.
# vi /usr/local/etc/webman/addwebuser.sh && chmod ug+x /usr/local/etc/webman/addwebuser.sh
#!/bin/bash
WEBROOT="/var/www"
GROUP=${1}
USER=${2}
PASSWORD=$(/usr/bin/pwgen --capitalize --numerals --secure 12 1)
LOG="$(dirname ${0})/add.log"
DATE=$(date +"%Y-%m-%d %H:%M:%S")
# Проверяем корректность вводимых данных и выводим подсказку в случае необходимости
[ ! "${1}" ] && { echo "Ожидаемый формат: ${0} \"group\" \"username\""; echo "Не указано название \"группы сайтов\". Операция создания пользователя прервана"; exit 1; }
[ ! -d "${WEBROOT}/${GROUP}" ] && { echo "Указанная \"группа сайтов\" отсутствует. Операция создания пользователя прервана"; exit 1; }
#
[ ! "${2}" ] && { echo "Ожидаемый формат: ${0} \"${GROUP}\" \"username\""; echo "Не указано имя пользователя. Операция создания такового прервана"; exit 1; }
[ $(echo "${USER}" | grep -i -c -e "^\(git\|www\)-") -ne "0" ] && { echo "Имя пользователя следует указывать без префиксов вроде \"git-\" или \"www-\". Операция создания пользователя прервана."; exit 1; }
[ $(echo "${USER}" | grep -i -c -e "-${GROUP}$") -ne "0" ] && { echo "Имя пользователя следует указывать без суффиксов вроде наименования группы \"${GROUP}\". Операция создания пользователя прервана."; exit 1; }
# Проверяем наличие пользователя и добавляем его только при необходимости
id ${USER}-${GROUP} > /dev/null 2>&1
if [ "${?}" -ne "0" ] ; then
echo >> "${LOG}"
echo "${DATE}:" >> "${LOG}"
# Создаём пользователя для доступа к "группе сайтов"
useradd --shell /bin/bash --no-create-home --home-dir ${WEBROOT}/${GROUP}/home --gid www-${GROUP} ${USER}-${GROUP}
if [ "${?}" -eq "0" ] ; then
# Задаём пароль созданной учётной записи
echo "${USER}-${GROUP}:${PASSWORD}" | chpasswd
# Явно вводим пользователя в группы доступа к необходимым ресурсам
usermod --gid www-${GROUP} ${USER}-${GROUP} > /dev/null 2>&1
usermod --append --groups www-data ${USER}-${GROUP} > /dev/null 2>&1
# Распечатываем сведения о созданном пользователе
echo
echo "Успешно создан пользователь для группы сайтов \"${GROUP}\":" | tee -a "${LOG}"
echo " SFTP Server: $(hostname)" | tee -a "${LOG}"
echo " SFTP User: ${USER}-${GROUP}" | tee -a "${LOG}"
echo " SFTP Password: ${PASSWORD}" | tee -a "${LOG}"
echo
fi
else
echo "Создаваемый пользователь \"${USER}-${GROUP}\" уже существует."
fi
exit ${?}
WEBROOT="/var/www"
GROUP=${1}
USER=${2}
PASSWORD=$(/usr/bin/pwgen --capitalize --numerals --secure 12 1)
LOG="$(dirname ${0})/add.log"
DATE=$(date +"%Y-%m-%d %H:%M:%S")
# Проверяем корректность вводимых данных и выводим подсказку в случае необходимости
[ ! "${1}" ] && { echo "Ожидаемый формат: ${0} \"group\" \"username\""; echo "Не указано название \"группы сайтов\". Операция создания пользователя прервана"; exit 1; }
[ ! -d "${WEBROOT}/${GROUP}" ] && { echo "Указанная \"группа сайтов\" отсутствует. Операция создания пользователя прервана"; exit 1; }
#
[ ! "${2}" ] && { echo "Ожидаемый формат: ${0} \"${GROUP}\" \"username\""; echo "Не указано имя пользователя. Операция создания такового прервана"; exit 1; }
[ $(echo "${USER}" | grep -i -c -e "^\(git\|www\)-") -ne "0" ] && { echo "Имя пользователя следует указывать без префиксов вроде \"git-\" или \"www-\". Операция создания пользователя прервана."; exit 1; }
[ $(echo "${USER}" | grep -i -c -e "-${GROUP}$") -ne "0" ] && { echo "Имя пользователя следует указывать без суффиксов вроде наименования группы \"${GROUP}\". Операция создания пользователя прервана."; exit 1; }
# Проверяем наличие пользователя и добавляем его только при необходимости
id ${USER}-${GROUP} > /dev/null 2>&1
if [ "${?}" -ne "0" ] ; then
echo >> "${LOG}"
echo "${DATE}:" >> "${LOG}"
# Создаём пользователя для доступа к "группе сайтов"
useradd --shell /bin/bash --no-create-home --home-dir ${WEBROOT}/${GROUP}/home --gid www-${GROUP} ${USER}-${GROUP}
if [ "${?}" -eq "0" ] ; then
# Задаём пароль созданной учётной записи
echo "${USER}-${GROUP}:${PASSWORD}" | chpasswd
# Явно вводим пользователя в группы доступа к необходимым ресурсам
usermod --gid www-${GROUP} ${USER}-${GROUP} > /dev/null 2>&1
usermod --append --groups www-data ${USER}-${GROUP} > /dev/null 2>&1
# Распечатываем сведения о созданном пользователе
echo
echo "Успешно создан пользователь для группы сайтов \"${GROUP}\":" | tee -a "${LOG}"
echo " SFTP Server: $(hostname)" | tee -a "${LOG}"
echo " SFTP User: ${USER}-${GROUP}" | tee -a "${LOG}"
echo " SFTP Password: ${PASSWORD}" | tee -a "${LOG}"
echo
fi
else
echo "Создаваемый пользователь \"${USER}-${GROUP}\" уже существует."
fi
exit ${?}
Пример использования.
Для создания нового сайта "site.example.net" в группе сайтов "group0" достаточно ввести следующие четыре команды:
# addwebgroup.sh group0
# addwebsite.sh group0 site.example.net
# addwebmysql.sh group0 site.example.net
# addwebmongodb.sh group0 site.example.net
# addwebuser.sh group0 userName
# addwebsite.sh group0 site.example.net
# addwebmysql.sh group0 site.example.net
# addwebmongodb.sh group0 site.example.net
# addwebuser.sh group0 userName
Напоминаю, что здесь приведены примеры автоматизации рутинных процедур создания новых "сводных площадок" групп сайтов и сайтов как таковых, поэтапно описанных в предыдущей основной инструкции по развёртыванию web-сервиса, и скрипты специально написаны так, чтобы не навредить при повторном запуске - они ничего не удаляют, лишь добавляя недостающее - таким образом их можно использовать в профилактических целях, восстанавливая возможно повреждённую опорную структуру сайта. Это означает, что конфигурационные файлы прикладных сервисов можно править так же и напрямую, без задействования приводимых здесь скриптов.