From a5fe856592cf011e21fadddd8520859bcc07f23f Mon Sep 17 00:00:00 2001 From: Victor Laborie Date: Mon, 14 Aug 2017 15:32:25 -0400 Subject: [PATCH] Add web-add script for Nginx/PHP-FPM --- scripts/fpm.conf.tpl | 18 ++ scripts/vhost-nginx.tpl | 41 +++ scripts/web-add-nginx.sh | 647 +++++++++++++++++++++++++++++++++++++ scripts/web-mail-nginx.tpl | 72 +++++ 4 files changed, 778 insertions(+) create mode 100644 scripts/fpm.conf.tpl create mode 100644 scripts/vhost-nginx.tpl create mode 100755 scripts/web-add-nginx.sh create mode 100644 scripts/web-mail-nginx.tpl diff --git a/scripts/fpm.conf.tpl b/scripts/fpm.conf.tpl new file mode 100644 index 0000000..ccb6d5f --- /dev/null +++ b/scripts/fpm.conf.tpl @@ -0,0 +1,18 @@ +[SED_LOGIN] +user = SED_LOGIN +group = SED_LOGIN + +listen = /var/run/php-fpm-SED_LOGIN.sock +listen.mode = 0660 +listen.owner = SED_LOGIN +listen.group = SED_LOGIN + +pm = ondemand +pm.max_children = 10 +pm.process_idle_timeout = 10s +pm.status_path = SED_STATUS + +php_admin_value[error_log] = /home/SED_LOGIN/phplog/php.log +php_admin_value[post_max_size] = 50M +php_admin_value[upload_max_filesize] = 50M +php_admin_value[max_execution_time] = 600 diff --git a/scripts/vhost-nginx.tpl b/scripts/vhost-nginx.tpl new file mode 100644 index 0000000..38b3b37 --- /dev/null +++ b/scripts/vhost-nginx.tpl @@ -0,0 +1,41 @@ +server { + server_name www.DOMAIN DOMAIN; + + listen 0.0.0.0:80; + listen [::]:80; +# listen 0.0.0.0:443 ssl http2; +# listen [::]:443 ssl http2; +# +# # Redirect HTTP to HTTPS +# if ( $scheme = http ) { +# return 301 https://$server_name$request_uri; +# } +# +# include /etc/nginx/ssl/LOGIN.conf; + + # Redirect alias to main server_name + if ($http_host != "www.DOMAIN") { + return 301 $scheme://www.DOMAIN$request_uri; + } + + access_log /home/LOGIN/log/access.log; + error_log /home/LOGIN/log/error.log; + + root /home/LOGIN/www; + index index.html index.php; + + # Set X-Forwarded-For, when you use reverse proxy such as Varnish. + #set_real_ip_from 127.0.0.1; + #real_ip_header X-Forwarded-For; + + location / { + try_files $uri $uri/ /index.php?$args; + # Symphony + #try_files $uri /app.php$is_args$args; + } + + location ~ \.php$ { + include snippets/fastcgi-php.conf; + fastcgi_pass unix:/var/run/php-fpm-LOGIN.sock; + } +} diff --git a/scripts/web-add-nginx.sh b/scripts/web-add-nginx.sh new file mode 100755 index 0000000..12db306 --- /dev/null +++ b/scripts/web-add-nginx.sh @@ -0,0 +1,647 @@ +#!/bin/bash + +# +# Gestion des comptes web et des hôtes virtuels Nginx +# +# Copyright (c) 2013 Evolix - Tous droits reserves +# + +# TODO +# - Gestion des quota +# - Possibilité de créer un compte FTP-only +# - Pouvoir spécifier le CONTACT_MAIL dans un fichier de conf +# - Traduire usage() en francais, ou l'inverse ?? + +set -e + +HOME="/root" +CONTACT_MAIL="equipe@evolix.fr" +WWWBOUNCE_MAIL="equipe@evolix.fr" +LOCAL_SCRIPT="/usr/share/scripts/evoadmin/web-add.local.sh" +PRE_LOCAL_SCRIPT="/usr/share/scripts/evoadmin/web-add.pre-local.sh" +TPL_VHOST="/usr/share/scripts/evoadmin/vhost-nginx.tpl" +TPL_AWSTATS="/usr/share/scripts/evoadmin/awstats.XXX.conf" +TPL_MAIL="/usr/share/scripts/evoadmin/web-mail-nginx.tpl" +TPL_FPM="/usr/share/scripts/evoadmin/fpm.conf.tpl" +VHOST_PATH="/etc/nginx/sites-available" +FPM_PATH="/etc/php/7.0/fpm/pool.d" +FPM_SERVICE_NAME="php7.0-fpm" +MAX_LOGIN_CHAR=16 +HOME_DIR="/home" +MYSQL_CREATE_DB_OPTS="" + +# Utiliser ce fichier pour redefinir la valeur des variables ci-dessus +config_file="/etc/evolix/web-add.conf" +[ -r $config_file ] && . $config_file + +usage() { + cat <&2 + +Usage: $0 COMMAND [ARG] + +add [ [OPTIONS] LOGIN WWWDOMAIN ] + + Create web account LOGIN. + No arguments starts interactive mode. + + -p PASSWD + FTP and SFTP password (default : random) + + -m DBNAME + Name of MySQL database (default : same as account) + + -P DBPASSWD + MySQL password (default : random) + + -l MAIL + Send summary email to MAIL + + -k SSHKEY + Use this SSH key + + -u UID + Force account UID (only in command line) + + -g GID + Force account GID (only in command line) + + -U UID + Force www-account UID (only in command line) + + -y + Don't ask for confirmation + + Example : web-add.sh add -m testdb testlogin testdomain.com + +del LOGIN [DBNAME] + + Delete account and all files related (Apache, Awstats, etc) + Archive home directory. + Remove MySQL database only if DBNAME is specified. + +list-vhost LOGIN + + List Apache vhost for user LOGIN + +EOT +} + +# +# Affiche un message d'erreur de validation +# +in_error() { + msg=$1 + cat >&2 </dev/null; then + in_error "Base de données déjà existante" + return 1 + fi +} + +validate_wwwdomain() { + wwwdomain=$1 + if [ -z "$wwwdomain" ]; then + in_error "Le nom de domaine est obligatoire" + return 1 + fi + return 0 +} + +validate_mail() { + return 0 +} + +step_ok() { + msg=$1 + echo "[OK] $msg" +} + +create_www_account() { + + # Vérifications + for filetocheck in $TPL_VHOST $TPL_AWSTATS $TPL_MAIL; do + if [ ! -f $filetocheck ]; then + in_error "Fichier inexistant : $filetocheck" + exit 1 + fi + done + + ############################################################################ + + if [ -f $PRE_LOCAL_SCRIPT ]; then + source $PRE_LOCAL_SCRIPT + fi + + step_ok "Exécution du pre-script spécifique" + + ############################################################################ + + if [ -z "$HOME_DIR_USER" ]; then + HOME_DIR_USER="$HOME_DIR/$in_login" + fi + + ############################################################################ + + if [ -d "$HOME_DIR_USER" ]; then + in_error "Ce compte existe deja (ou il a mal été effacé)" + return 1 + fi + + ############################################################################ + + # Force UID GID if specified + + [ -n "$in_uid" ] && OPT_UID="--uid" && OPT_UID_ARG="$in_uid" + [ -n "$in_gid" ] && OPT_GID="--gid" && OPT_GID_ARG="$in_gid" + [ -n "$in_wwwuid" ] && OPT_WWWUID="--uid" && OPT_WWWUID_ARG="$in_wwwuid" + + ############################################################################ + + + /usr/sbin/adduser --gecos "User $in_login" --disabled-password "$in_login" \ + --shell /bin/bash $OPT_UID $OPT_UID_ARG --force-badname \ + --home "$HOME_DIR_USER" >/dev/null + [ -z "$in_sshkey" ] && echo "$in_login:$in_passwd" | chpasswd --md5 + [ -z "$in_sshkey" ] || [ -n "$HOME_DIR_USER" ] \ + && mkdir "$HOME_DIR_USER/.ssh" \ + && echo "$in_sshkey" > "$HOME_DIR_USER/.ssh/authorized_keys" \ + && chmod -R u=rwX,g=,o= "$HOME_DIR_USER/.ssh/authorized_keys" \ + && chown -R "$in_login":"$in_login" "$HOME_DIR_USER/.ssh" + + # Adding user www-data to group $in_login. + # And primary group www-data for $in_login. + adduser www-data $in_login + usermod -g www-data $in_login + # Authorize user to connect by SSH. + sed -i "s/^AllowUsers .*/& $in_login/" /etc/ssh/sshd_config + /etc/init.d/ssh reload + + step_ok "Création des utilisateurs" + + ############################################################################ + + echo "$login: $WWWBOUNCE_MAIL" >> /etc/aliases + newaliases + + step_ok "Alias mail" + + ############################################################################ + + chmod 750 $HOME_DIR_USER/ + + # Répertoires par défaut + mkdir -p $HOME_DIR_USER/{log,www,awstats} + chown $in_login:$in_login $HOME_DIR_USER/www + chgrp $in_login $HOME_DIR_USER/{log,awstats} + chmod 750 $HOME_DIR_USER/{log,www,awstats} + + # Ajout des logs par defaut + touch $HOME_DIR_USER/log/access.log + touch $HOME_DIR_USER/log/error.log + touch $HOME_DIR_USER/log/php.log + chgrp $in_login $HOME_DIR_USER/log/access.log + chgrp $in_login $HOME_DIR_USER/log/error.log + chown $in_login:$in_login $HOME_DIR_USER/log/php.log + chmod 640 $HOME_DIR_USER/log/access.log + chmod 640 $HOME_DIR_USER/log/error.log + chmod 640 $HOME_DIR_USER/log/php.log + + step_ok "Création du répertoire personnel" + + ############################################################################ + + random=$RANDOM + + cat $TPL_VHOST | \ + sed -e " + s/DOMAIN/${in_wwwdomain}/g; + s/LOGIN/${in_login}/g;" > ${VHOST_PATH}/$in_login + # On active aussi example.com si le domaine commence par "www." comme + # www.example +# if echo $in_wwwdomain | grep '^www.' > /dev/null; then +# subweb=$(echo $in_wwwdomain | sed -e "s/www.//") +# sed -i -e "s/^\(.*\)#\(ServerAlias\).*$/\1\2 $subweb/" $vhostfile +# fi + + ln -s /etc/nginx/sites-available/$in_login \ + /etc/nginx/sites-enabled/$in_login + + /etc/init.d/nginx restart + + step_ok "Configuration de Nginx + restart" + + ############################################################################ + + cat $TPL_FPM | \ + sed -e "s/SED_LOGIN/${in_login}/g;" > ${FPM_PATH}/${in_login}.conf + step_ok "Creation du pool PHP-FPM" + + ############################################################################ + + cat $TPL_AWSTATS | \ + sed -e "s/XXX/$in_login/ ; s/SERVERNAME/$in_wwwdomain/ ; s#HOME_DIR#$HOME_DIR#" \ + > /etc/awstats/awstats.$in_login.conf + chmod 644 /etc/awstats/awstats.$in_login.conf + + VAR=`grep -v "^#" /etc/cron.d/awstats |tail -1 | cut -d " " -f1` + if [ "$VAR" = "" ] || [ $VAR -ge 59 ]; then + VAR=1 + else + VAR=$(($VAR +1)) + fi + + echo "$VAR * * * * root umask 033; [ -x /usr/lib/cgi-bin/awstats.pl -a -f /etc/awstats/awstats.$in_login.conf -a -r $HOME_DIR_USER/log/access.log ] && /usr/lib/cgi-bin/awstats.pl -config=$in_login -update >/dev/null" >> /etc/cron.d/awstats + + step_ok "Activation d'Awstats" + + ############################################################################ + + if [ "$in_dbname" ]; then + echo "CREATE DATABASE \`$in_dbname\` $MYSQL_CREATE_DB_OPTS;" | mysql + echo "CREATE DATABASE \`staging_${in_dbname}\` $MYSQL_CREATE_DB_OPTS;" | mysql + echo "GRANT ALL PRIVILEGES ON \`$in_dbname\`.* TO \`$in_login\`@localhost IDENTIFIED BY '$in_dbpasswd';" | mysql + echo "GRANT ALL PRIVILEGES ON \`staging_${in_dbname}\`.* TO \`$in_login\`@localhost IDENTIFIED BY '$in_dbpasswd';" | mysql + echo "FLUSH PRIVILEGES;" | mysql + + my_cnf_file="$HOME_DIR_USER/.my.cnf" + cat >$my_cnf_file <<-EOT + [client] + user = $in_login + password = "$in_dbpasswd" + + [mysql] + database = $in_dbname +EOT + chown $in_login $my_cnf_file + chmod 600 $my_cnf_file + + step_ok "Création base de données et compte MySQL" + fi + + ############################################################################ + + cat $TPL_MAIL | \ + sed -e "s/LOGIN/$in_login/g ; s/SERVERNAME/$in_wwwdomain/ ; s/PASSE1/$in_passwd/ ; s/PASSE2/$in_dbpasswd/ ; s/RANDOM/$random/ ; s/QUOTA/$quota/ ; s/RCPTTO/$in_mail/ ; s/DBNAME/$in_dbname/ ; s#HOME_DIR#$HOME_DIR#"| \ + /usr/lib/sendmail -oi -t -f "$CONTACT_MAIL" + + step_ok "Envoi du mail récapitulatif" + + ############################################################################ + + fpm_status=$(echo -n $in_login | md5sum | cut -d' ' -f1) + cat < /etc/munin/plugin-conf.d/phpfpm_${in_login}_ + +[phpfpm_${in_login}_*] +env.url http://munin:%d/fpm_status_$fpm_status +env.ports 80 +env.phpbin php-fpm +env.phppool $in_login +EOT + for name in average connections memory processes status; do + ln -s /usr/local/share/munin/plugins/phpfpm_${name} \ + /etc/munin/plugins/phpfpm_${in_login}_${name} + done + cat <> /etc/nginx/evolinux.d/munin-plugins.conf + +# $in_login FPM Status page. Secret part is md5 of pool name. +location ~ ^/fpm_status_${fpm_status}$ { + include fastcgi_params; + fastcgi_pass unix:/var/run/php-fpm-${in_login}.sock; + fastcgi_param SCRIPT_FILENAME \$fastcgi_script_name; + allow 127.0.0.1; + deny all; +} +EOT + sed -i "s#SED_STATUS#/fpm_status_${fpm_status}#" \ + ${FPM_PATH}/${in_login}.conf + /etc/init.d/nginx reload + /etc/init.d/${FPM_SERVICE_NAME} reload + /etc/init.d/munin-node restart + + step_ok "Configuration plugin php-fpm pour munin" + ############################################################################ + + if [ -f $LOCAL_SCRIPT ]; then + source $LOCAL_SCRIPT + fi + + step_ok "Exécution du script spécifique" + + ############################################################################ + + DATE=$(date +"%Y-%m-%d") + echo "$DATE [web-add.sh] Ajout $in_login" >> /var/log/evolix.log +} + +op_del() { + if [ $# -lt 1 ]; then + usage + exit 1 + else + login=$1 + if [ $# -eq 2 ]; then + dbname=$2 + fi + fi + + echo "Deleting account $login. Continue ?" + read + + set -x + deluser www-data $login + userdel $login + groupdel $login + sed -i.bak "/^$login:/d" /etc/aliases + + sed -i "s/^\(AllowUsers .*\)$login/\1/" /etc/ssh/sshd_config + /etc/init.d/ssh reload + + if [ -d "$HOME_DIR/$login" ]; then + mv -i $HOME_DIR/$login $HOME_DIR/$login.`date '+%Y%m%d-%H%M%S'`.bak + else + echo "warning : $HOME_DIR/$login does not exist" + fi + + rm /etc/nginx/sites-{available,enabled}/$login + rm /etc/awstats/awstats.$login.conf + rm /etc/munin/plugins/phpfpm_${in_login}* + sed -i.bak "/-config=$login/d" /etc/cron.d/awstats + nginx -t + set +x + + if [ -n "$dbname" ]; then + echo "Deleting mysql DATABASE $dbname and mysql user $login. Continue ?" + read + + set -x + echo "DROP DATABASE $dbname; delete from mysql.user where user='$login' ; FLUSH PRIVILEGES;" | mysql + set +x + fi +} + +arg_processing() { + + # Détermination de la commande + + if [ $# -lt 1 ]; then + usage + else + commandname=$1 + shift + + case "$commandname" in + add) + op_add $* + ;; + del) + op_del $* + ;; + list-vhost) + op_listvhost $* + ;; + *) + usage + ;; + esac + fi +} + +op_listvhost() { + if [ $# -eq 1 ]; then + configlist="$VHOST_PATH/$1"; + else + configlist="$VHOST_PATH/*"; + fi + + + for configfile in $configlist; do + if [ -r "$configfile" ]; then + servername=`awk '/^[[:space:]]*ServerName (.*)/ { print $2 }' $configfile | head -n 1` + serveraliases=`perl -ne 'print $1 if /^[[:space:]]*ServerAlias (.*)/' $configfile | head -n 1` + serveraliases=`echo $serveraliases | sed 's/ \+/, /g'` + userid=`awk '/^[[:space:]]*AssignUserID.*/ { print $3 }' $configfile | head -n 1` + if [ "$servername" ] && [ "$userid" ]; then + configid=`basename $configfile` + echo "$userid:$configid:$servername:$serveraliases" + fi + fi + done +} + +op_add() { + + # + # Mode interactif + # + if [ $# -eq 0 ]; then + echo + echo "Ajout d'un compte WEB" + echo + + until [ "$in_login" ]; do + echo -n "Entrez le login du nouveau compte : " + read tmp + if validate_login "$tmp"; then + in_login="$tmp" + fi + done + + until [ "$in_passwd" ]; do + echo -n "Entrez le mot de passe FTP/SFTP/SSH (ou vide pour aleatoire) : " + read -s tmp + echo + + if [ -z "$tmp" ]; then + tmp=`gen_random_passwd` + fi + + if validate_passwd "$tmp"; then + in_passwd="$tmp" + fi + done + + echo -n "Voulez-vous aussi un compte/base MySQL ? [Y|n] " + read confirm + + if [ "$confirm" != "n" ] && [ "$confirm" != "N" ]; then + until [ "$in_dbname" ]; do + echo -n "Entrez le nom de la base de donnees ($in_login par defaut) : " + read tmp + + if [ -z "$tmp" ]; then + tmp=$in_login + fi + + if validate_dbname "$tmp"; then + in_dbname="$tmp" + fi + done + + until [ "$in_dbpasswd" ]; do + echo -n "Entrez le mot de passe MySQL (ou vide pour aleatoire) : " + read -s tmp + echo + + if [ -z "$tmp" ]; then + tmp=`gen_random_passwd` + fi + + if validate_passwd "$tmp"; then + in_dbpasswd="$tmp" + fi + done + fi + + until [ "$in_wwwdomain" ]; do + echo -n "Entrez le nom de domaine web (ex: foo.example.com) : " + read tmp + if validate_wwwdomain "$tmp"; then + in_wwwdomain="$tmp" + fi + done + + until [ "$in_mail" ]; do + echo -n "Entrez votre adresse mail pour recevoir le mail de creation ($CONTACT_MAIL par défaut) : " + read tmp + if [ -z "$tmp" ]; then + tmp="$CONTACT_MAIL" + fi + if validate_mail "$tmp"; then + in_mail="$tmp" + fi + done + + # + # Mode non interactif + # + else + while getopts hyp:m:P:w:l:k:u:g:U: opt; do + case "$opt" in + p) + in_passwd=$OPTARG + ;; + m) + in_dbname=$OPTARG + ;; + P) + in_dbpasswd=$OPTARG + ;; + l) + in_mail=$OPTARG + ;; + k) + in_sshkey=$OPTARG + ;; + y) + force_confirm=1 + ;; + u) + in_uid=$OPTARG + ;; + g) + in_gid=$OPTARG + ;; + U) + in_wwwuid=$OPTARG + ;; + h) + usage + exit 1 + ;; + ?) + usage + exit 1 + ;; + esac + done + + shift $(($OPTIND - 1)) + if [ $# -ne 2 ]; then + usage + exit 1 + else + in_login=$1 + in_wwwdomain=$2 + validate_login $in_login || exit 1 + [ -z "$in_passwd" ] && [ -z "$in_sshkey" ] && in_passwd=`gen_random_passwd` + [ -z "$in_sshkey" ] && ( validate_passwd $in_passwd || exit 1 ) + [ -n "$in_dbname" ] && ( validate_dbname $in_dbname || exit 1 ) + [ -z "$in_dbpasswd" ] && [ -n "$in_dbname" ] && in_dbpasswd=`gen_random_passwd` + [ -n "$in_dbname" ] && ( validate_passwd $in_dbpasswd || exit 1 ) + validate_wwwdomain $in_wwwdomain || exit 1 + [ -z "$in_mail" ] && in_mail=$CONTACT_MAIL + validate_mail $in_mail || exit 1 + fi + fi + + echo + echo "----------------------------------------------" + echo "Nom du compte : $in_login" + echo "Mot de passe : $in_passwd" + if [ "$in_dbname" ]; then + echo "Base de données MySQL : $in_dbname" + echo "Mot de passe MySQL : $in_dbpasswd" + fi + echo "Nom de domaine : $in_wwwdomain" + echo "Envoi du mail récapitulatif à : $in_mail" + echo "----------------------------------------------" + echo + + if [ -z "$force_confirm" ]; then + echo -n "Confirmer la création ? [y/N] : " + read tmp + echo + if [ "$tmp" != "y" ] && [ "$tmp" != "Y" ]; then + echo "Annulation..." + echo + exit 1 + fi + fi + + create_www_account + echo + echo " => Compte $in_login créé avec succès" + echo +} + +# Point d'entrée +arg_processing $* + diff --git a/scripts/web-mail-nginx.tpl b/scripts/web-mail-nginx.tpl new file mode 100644 index 0000000..18f4252 --- /dev/null +++ b/scripts/web-mail-nginx.tpl @@ -0,0 +1,72 @@ +From: Equipe Evolix +To: RCPTTO +Bcc: alert3@evolix.fr +Subject: Parametres hebergement web : LOGIN + +Bonjour, + +Votre compte d'hebergement web a ete cree. + +********************************** +* CONNEXION SFTP/SSH +********************************** + +NOM DU SERVEUR : %SERVER_NAME% +USER : LOGIN +PASSWORD : PASSE1 + +***************************************** +* Details sur l'environnement NginX/PHP +***************************************** + +URL du site : +http://SERVERNAME + +Repertoire de connexion : HOME_DIR/LOGIN/ +Repertoire pour site web : HOME_DIR/LOGIN/www/ + +PHP tourne en www-data:www-data c'est-a-dire qu'il a acces +uniquement *en lecture* aux differents fichiers/repertoires (a condition +d'avoir 'g=rx' sur les repertoires et 'g=r' sur les fichiers ce qui est le +comportement par defaut). + +Lorsqu'on a besoin d'autoriser *l'ecriture* pour certains fichiers/repertoires, +il suffit d'ajouter le droit 'g+w'. + +*********************************** +* MySQL +*********************************** + +SERVEUR : 127.0.0.1 +PORT DU SERVEUR : 3306 +USER : LOGIN +PASSWORD : PASSE2 +NOM BASE : DBNAME +URL interface d'admin : +%PMA_URL% + +*********************************** +* Rappels divers +*********************************** + +Votre nom de domaine doit etre configure pour pointer sur l'adresse IP +(enregistrement DNS A) ou etre un alias de (enregistrement DNS CNAME). + +Si vous avez besoin de faire des tests, vous devez ajouter la ligne suivante au +fichier "/etc/hosts" sous Linux/Unix ou au fichier "system32\drivers\etc\hosts" +sous Windows : +%SERVER_ADDR% SERVERNAME + +Attention, par defaut, toutes les connexions vers l'exterieur sont bloquees. Si +vous avez besoin de recuperer des donnees a l'exterieur (flux RSS, BDD externe, +etc.), contactez nous afin de mettre en oeuvre les autorisations necessaires. + +Si vous desirez mettre en place des parametres particuliers pour votre site +(PHP, etc.) ou pour tout autre demande (scripts en crontab, etc.), n'hesitez +pas a nous contacter a l'adresse %MAIL_STANDARD% (ou %MAIL_URGENT% si +votre demande est urgente). + +Cordialement, +-- +Equipe Evolix +Evolix http://www.evolix.fr/