Быстрое создание виртуальных хостов Apache при помощи bash скрипта

Не так давно переехал с VPS на выделенные сервер и передо мной остро встал вопрос по переносу сайтов со старого сервера на новый, а именно по быстрому созданию виртуальных хостов и баз данных. Конечно же в довесок к серверу шла панель управления ISPmanager, но в этом случае мне не нравилось две вещи:
  • Панель делает все за тебя, а мне хочется прокачать скил в области адмиистрирования.
  • Не нравится способ создания сайтов через панель, а именно создаваемые пути до папки с сайтом (/var/www/user_name/data/www/site.ru)

В силу этого решил все настраивать ручками. Про установку Apache и php, писать не буду, так как и на Хабре и в интернете очень много материалов по данной теме. Нас же больше интересует быстрое создание пользователя, виртуального хоста, и базы данных. Кому интересно прошу в подкат.

Логику решил разделить на два скрипта.
  • Создает пользователя и в его папке добавляет сайты. Если пользователя нет, то он создается, если есть, то создается только сайт.
  • Второй скрипт создает базу данных и пользователя для базы, присваивая ему привилегии на данную базу.

Немного условий для скриптов.
  • Оба скрипта генерируют пароли по умолчанию.
  • Конфиги виртуальных хостов должны лежать в /etc/apache2/vhosts.
  • Все действия должны производиться только из под суперпользователя

Скрипт для создания нового виртуального хоста (/home/addsite)
#!/bin/bash
IP_ADDRESS="1.2.3.4"

APACHE2_DIR="/etc/apache2"

UID_ROOT=0

if [ "$UID" -ne "$UID_ROOT" ]; then
  echo "$0 - Requires root privileges"
  exit 1
fi

function is_user(){
    local check_user="$1";
    grep "$check_user:" /etc/passwd >/dev/null
    if [ $? -ne 0 ]; then
 #echo "NOT HAVE USER"
 return 0
    else
 #echo "HAVE USER"
 return 1
    fi
}
 
function generate_pass(){
    CHARS="0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz!@#$%^&*()-_=+\\|/"
    LENGTH="8"
    while [ "${n:=1}" -le "$LENGTH" ] ; do
	PASSWORD="$PASSWORD${CHARS:$(($RANDOM%${#CHARS})):1}"
        let n+=1
    done
    echo $PASSWORD
}
 
function is_yes(){
#TODO - add check 3-rd parameter for set default ansver (if press enter)
    while true
    do
 echo -n "Yes or No[Y/n]:"
 read  x
 if [ -z "$x" ]
 then
     return 0; #defaul answer: Yes
 fi
 case "$x" in
 y |Y |yes |Д |д |да ) return 0;;
 n |N |no |Н |н |нет ) return 1;;
# * ) ; # asc again
 esac
    done
}

function create_user(){
    local login="$1"
    local password="$2"
    `useradd -m -s /bin/bash $login`
    #set password
    echo -e "$password\n$password\n" | passwd $login >> /dev/null
}

USER_NAME=$1

echo -n "Check user name $USER_NAME: "
if( is_user "$USER_NAME" )then
    USER_PASSWORD="$(generate_pass)"
    echo "-----------------------------------"
    echo "User name    : $USER_NAME"
    echo "User password: $USER_PASSWORD"
    echo "-----------------------------------"
    echo -n "Continue? "
    if(! is_yes) then
        exit;
    fi
    echo "--- create user ---"
    create_user "$USER_NAME" "$USER_PASSWORD"
fi

if [ $# -eq 2 ]; then
    if [ "$2" != "delete" ]; then
        SITE_NAME=$2
        
        mkdir /home/$USER_NAME/$SITE_NAME
        mkdir /home/$USER_NAME/$SITE_NAME/www
        mkdir /home/$USER_NAME/$SITE_NAME/logs
        mkdir /home/$USER_NAME/$SITE_NAME/tmp
        mkdir /home/$USER_NAME/$SITE_NAME/cgi-bin
        
        hostConf="
<VirtualHost ${IP_ADDRESS}:80>
        ServerName $SITE_NAME
        ServerAlias www.$SITE_NAME
        ServerAdmin webmaster@$SITE_NAME

        AddDefaultCharset utf-8
        AssignUserID ${USER_NAME} ${USER_NAME}

        DocumentRoot /home/$USER_NAME/$SITE_NAME/www
        CustomLog log combined
        ErrorLog /home/$USER_NAME/$SITE_NAME/logs/error.log
        DirectoryIndex index.php index.html

        ScriptAlias /cgi-bin/ /home/$USER_NAME/$SITE_NAME/cgi-bin
        <FilesMatch \"\\.ph(p[3-5]?|tml)$\">
                SetHandler application/x-httpd-php
        </FilesMatch>
        <FilesMatch \"\\.phps$\">
                SetHandler application/x-httpd-php-source
        </FilesMatch>
        php_admin_value upload_tmp_dir "/home/$USER_NAME/$SITE_NAME/tmp"
        php_admin_value session.save_path "/home/$USER_NAME/$SITE_NAME/tmp"
        php_admin_value open_basedir "/home/$USER_NAME/$SITE_NAME/www:."
</VirtualHost>
<Directory /home/$USER_NAME/$SITE_NAME/www>
        Options +Includes +ExecCGI
        php_admin_flag engine on
</Directory>
        "

        touch ${APACHE2_DIR}/vhosts/${SITE_NAME}.conf
        echo "$hostConf" >> ${APACHE2_DIR}/vhosts/${SITE_NAME}.conf
        touch //home/$USER_NAME/$SITE_NAME/www/index.php
        echo "<?php phpinfo() ?>" >> /home/$USER_NAME/$SITE_NAME/www/index.php
        
        chown $USER_NAME:$USER_NAME /home/$USER_NAME/$SITE_NAME/*
        
        service apache2 restart
    fi
fi;

#display information
echo "*****************************************"
echo "* Profit!"
echo "*****************************************"

В общем ни чего сложного, в самом начале задаем ip адрес сервера и папку где у нас лежат настройки апача. Не забываем добавить права на исполнения файла
chmod -x /home/addsite

Для того чтобы апач мог подцепить наши конфиги в конец главного конфигурационого файла добавляем
Include vhosts/

Запускаем скрипт просто
/home/addsite user_name site.ru

Скрипт создаст пользователя, виртуальный хост и перезапустит апач. И конечно же не забудет показать пароль для вновь созданного пользователя.
Создание базы данных. Меня немного напрягало создание базы из phpMyAdmin, надо сначала создать базу, потом пользователя и еще не забыть добавить привелегии бд, новому пользователю, вот и упрощаем себе жизнь (/home/addbd).
#!/bin/bash

MYSQL_PASS="derev123blog"

UID_ROOT=0

if [ "$UID" -ne "$UID_ROOT" ]; then
  echo "$0 - Requires root privileges"
  exit 1
fi

function generate_pass(){
    CHARS="0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz!@#$%^&*()-_=+\\|/"
    LENGTH="8"
    while [ "${n:=1}" -le "$LENGTH" ] ; do
 PASSWORD="$PASSWORD${CHARS:$(($RANDOM%${#CHARS})):1}"
        let n+=1
    done
    echo $PASSWORD
}

function is_running(){
    local result="$(ps -A|grep $1|wc -l)"
    if [[ $result -eq 0 ]]; then
 return 1
    else
 return 0
    fi
}

if [ $# -eq 1 ]; then
    echo -n "Check MySQL status: "
    if(is_running mysqld)then
        echo "OK [Running]";
        DB_NAME=$1
        DB_PASSWORD="$(generate_pass)"
        mysql -uroot -p${MYSQL_PASS} --execute="create database ${DB_NAME};"
        mysql -uroot -p${MYSQL_PASS} --execute="GRANT ALL PRIVILEGES ON ${DB_NAME}.* TO '${DB_NAME}'@'localhost' IDENTIFIED by '${DB_PASSWORD}'  WITH GRANT OPTION;"
    else
        echo "Error: need start mysql daemon!"
        exit
    fi
fi;

#display information
echo "*****************************************"
echo "* Data base name: ${DB_NAME}"
echo "* Data base user: ${DB_NAME}"
echo "* User password: ${DB_PASSWORD}"
echo "* Profit!"
echo "*****************************************"

В самом начале скрипта задаем пароль для пользователя root из MySQL. Запускаем командой
/home/addsite bd_name

Создастся база данных и пользователь и вывидутся данные для подключения.
Так же можно добавить оба файлв в дирректорию /bin для быстрого вызова данных комманд
cp /home/addsite /bin/addsite
cp /home/addbd /bin/addbd

Вроде как все. Надеюсь данный способ создания виртуальных хостов упростит жизнь пользователям так же как и мне.
Поделиться публикацией
Ой, у вас баннер убежал!

Ну. И что?
Реклама
Комментарии 21
    0
    ну, почему бы и нет :)
      +3
      cp default_site_skeleton.conf my_brand_new_site.conf
      vim my_brand_new_site.conf
      :%s/skel_site_name/new_site_name/g
      :wq
      service httpd restart
      не вариант?)
        0
        Зачем все усложнять? :)

        cp skel.conf host.conf
        sed -i s/skel_site_name/new_site_name/g
        service apache2 restart
        
        +1
        вместо генератора паролей, как вариант:
        cat /dev/urandom | base64 | head -n1 -c10 && echo \n
        


        mysql не обязательно запускать от root
          +1
          Может просто pwgen?
            –1
            как вариант, но создает несколько десятков паролей, как-то не могу додумать как применить один из
              +1
              Смотрим man pwgen:
              man pwgen
              pwgen(1) — Linux man page

              Name

              pwgen — generate pronounceable passwords
              Synopsis

              pwgen [ OPTION ] [ pw_length ] [ num_pw ]
              Description

              The pwgen program generates passwords which are designed to be easily memorized by humans, while being as secure as possible. Human-memorable passwords are never going to be as secure as completely completely random passwords. In particular, passwords generated by pwgen without the -s option should not be used in places where the password could be attacked via an off-line brute-force attack. On the other hand, completely randomly generated passwords have a tendency to be written down, and are subject to being compromised in that fashion.
              The pwgen program is designed to be used both interactively, and in shell scripts. Hence, its default behavior differs depending on whether the standard output is a tty device or a pipe to another program. Used interactively, pwgen will display a screenful of passwords, allowing the user to pick a single password, and then quickly erase the screen. This prevents someone from being able to «shoulder surf» the user's chosen password.

              When standard output (stdout) is not a tty, pwgen will only generate one password, as this tends to be much more convenient for shell scripts, and in order to be compatible with previous versions of this program.

              In addition, for backwards compatibility reasons, when stdout is not a tty and secure password generation mode has not been requested, pwgen will generate less secure passwords, as if the -0A options had been passed to it on the command line. This can be overriden using the -nc options. In the future, the behavior when stdout is a tty may change, so shell scripts using pwgen should explicitly specify the -nc or -0A options. The latter is not recommended for security reasons, since such passwords are far too easy to guess.

              Options

              -0, --no-numerals
              Don't include numbers in the generated passwords.
              -1
              Print the generated passwords one per line.
              -A, --no-capitalize
              Don't bother to include any capital letters in the generated passwords.
              -a, --alt-phonics
              This option doesn't do anything special; it is present only for backwards compatibility.
              -B, --ambiguous
              Don't use characters that could be confused by the user when printed, such as 'l' and '1', or '0' or 'O'. This reduces the number of possible passwords significantly, and as such reduces the quality of the passwords. It may be useful for users who have bad vision, but in general use of this option is not recommended.
              -c, --capitalize
              Include at least one capital letter in the password. This is the default if the standard output is a tty device.
              -C
              Print the generated passwords in columns. This is the default if the standard output is a tty device.
              -N, --num-passwords=num
              Generate num passwords. This defaults to a screenful if passwords are printed by columns, and one password.
              -n, --numerals
              Include at least one number in the password. This is the default if the standard output is a tty device.
              -H, --sha1=/path/to/file[#seed]
              Will use the sha1's hash of given file and the optional seed to create password. It will allow you to compute the same password later, if you remember the file, seed, and pwgen's options used. ie: pwgen -H ~/your_favorite.mp3#your@email.com gives a list of possibles passwords for your pop3 account, and you can ask this list again and again.
              WARNING:
              The passwords generated using this option are not very random. If you use this option, make sure the attacker can not obtain a copy of the file. Also, note that the name of the file may be easily available from the ~/.history or ~/.bash_history file.
              -h, --help
              Print a help message.
              -s, --secure
              Generate completely random, hard-to-memorize passwords. These should only be used for machine passwords, since otherwise it's almost guaranteed that users will simply write the password on a piece of paper taped to the monitor…
              -v, --no-vowels
              Generate random passwords that do not contain vowels or numbers that might be mistaken for vowels. It provides less secure passwords to allow system administrators to not have to worry with random passwords accidentally contain offensive substrings.
              -y, --symbols
              Include at least one special character in the password.
              Author

              This version of pwgen was written by Theodore Ts'o <tytso@alum.mit.edu>. It is modelled after a program originally written by Brandon S. Allbery, and then later extensively modified by Olaf Titz, Jim Lynch, and others. It was rewritten from scratch by Theodore Ts'o because the original program was somewhat of a hack, and thus hard to maintain, and because the licensing status of the program was unclear.
              See Also

              passwd(1)
              Referenced By

              pass(1), secpwgen(1)

              Видим второй параметр num_pw. То есть для получения одного пароля достаточно набрать
              pwgen 16 1
              
            –3
            Генерация пароля проще, обязательно запомню, спасибо за подсказку.
            mysql не обязательно запускать от root

            но у пользователя все равно должны быть привилегии на создание бд и создание пользователя
              0
              да конечно, но не у пользователя системы, а у пользователя mysql, тот пользователь что указан тут — mysql -uroot
            0
            Для справедливости стоит отметить и более родной способ создания автохостов, который не требует перезапуска сервера: httpd.apache.org/docs/2.2/vhosts/mass.html
              0
              Кстати, а апач нативно еще не научился конфиги хранить в sql базе?
                0
                А еще можно добавить CustomLog + LogRotate, если хостов много.
                  0
                  Ну не знаю, по моему у всех такое уже есть так или иначе =) Мой вариант у меня уже года 4 без особых изменений кочует из системы в систему.
                    0
                    Тоже не плох вариант, но только для локального использования или же каждый раз открывать файл и редактировать доменную зону. Не создается пользователь и ставятся права на папку запись для всех.
                      0
                      Для локального пользования и делался =)

                      С доменом для локального тестирования (ps от prostosergik) один раз конфуз был. Делали сайт израильским заказчикам. И как-то дошло дело до скриншотов с моей девелоперской машины. Те посмотрели и устроили нам скандал, мол, вы что, издеваетесь использовать палестинский домен?
                    0
                    Один я заметил опечатку «при помощи bash скрита»?
                      0
                      Исправил, спасибо.
                      +3
                      Человеки, а чем вам vhost_alias_module не устраивает?
                      # VirtualHosts IP Definition
                      NameVirtualHost 192.168.0.1:80
                      
                      LogFormat "%V %h %l %u %t \"%r\" %s %b" vcommon
                      
                      # MassVirtualhosting
                      <VirtualHost 192.168.0.1:80>
                          ServerName example.com
                          ServerAlias *.%0
                          # DocumentRoot and CGI-BIN root
                          VirtualDocumentRoot /web/virtualhosts/%0/httpdocs
                          VirtualScriptAlias  /web/virtualhosts/%0/cgi-bin
                          # Logs
                          CustomLog /web/logs/%0-access.log vcommon
                          ErrorLog  /web/logs/allhosts-error.log
                      </VirtualHost>
                      


                      Насоздавал каталогов в /web/virtualhosts/имя_домена и have fun =)
                      0
                      В ISPmanager, есть такая штука как DefaultHomeDir если вам чем то не устраивает папка var под сайты.

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

                      Кстати, рекомендую также поставить nginx.

                      P.S. учитывая что вы указывайте не стандартные пути для хранения ErrorLog логов, перенастраивали ли вы их ротацию? место на диске имеет свойство заканчиваться.
                        0
                        Перестраиваю только для постоянных сайтов. А так как большинство сайтов на моем сервере живут только до момента переноса к клиенту, то не вижу смысл перестраивать ротацию. Потом сайты успешно архивируются и отправляются в папочку с бэкапами, сам же хост удаляется.

                        P.S. сейчас еще появился скрит немного переделан под то что бы в случае чего можно было удалить не нужный сайт. На досуге обновлю статью

                      Только полноправные пользователи могут оставлять комментарии. Войдите, пожалуйста.

                      Самое читаемое