L’esercizio in bash che propongo stavolta consiste nell’automatizzare la configurazione di un hosting sul nostro server Apache, ovvero la configurazione del VirtualHost, di un nuovo utente e database MySQL e di un accesso FTP.
L’obiettivo è quello di arrivare ad avere uno script utilizzabile nel modo seguente:
1 |
sudo ./addhosting.sh -u pippo -d sito-diprova.com --dbadmin root --dbpass passwor |
I parametri che vogliamo passare al nostro script sono:
- -u: il nome dell’utente che vogliamo creare
- -d: il nome del dominio che vogliamo registrare come VirtualHost di Apache
- –dbadmin: il nome utente dell’amministratore del database, quello in grado di creare altri utenti all’interno del MySQL
- –dbpass: la password del suddetto utente del database MySQL
Questo ci darà occasione per esplorare diverse caratteristiche di bash.
Anzitutto vogliamo acquisire ed elaborare i vari parametri passati al nostro script. Finora abbiamo visto che i parametri possono essere letti usando $# per contare il numero di argomenti, $0 per visualizzare il nome dello script, $1 per prendere il primo parametro, $2 per prendere il secondo e via discorrendo.
Adesso vogliamo prendere un numero indefinito di argomenti e parametrizzarli, vediamo come fare:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 |
while [[ $# -gt 0 ]] do parametro="$1" case $parametro in -d|--domain) dominio="$2" shift shift ;; -u|--user) utente="$2" shift shift ;; --dbadmin) dbadmin="$2" shift shift ;; --dbpass|--dbpassword) dbpass="$2" shift shift ;; -h|--help) aiuto=true shift ;; -f|--fix) ripristina=true shift ;; *) shift ;; esac done |
Usiamo quindi un ciclo while che continuerà ad andare avanti finché il numero di parametri passati allo script non sarà 0. Quindi acquisiamo il primo parametro $1 e lo passiamo ad una variabile chiamata parametro. Utilizziamo adesso un interruttore (case…esac) per cui in base al valore di parametro si attiveranno diverse opzioni.
Ciascuna opzione è scritta nella forma
1 2 3 4 |
valore) ... ... ;; |
Qui al posto di valore può essere un singolo valore oppure un gruppo diviso da |, nella maniera di valore1|valore2. Al posto dei puntini ci va del codice a piacere che vogliamo attivare in base alla variabile. Il principio, concettualmente, è identico a quello della realizzazione del programma in bash con menu.
Il comando shift, in bash, rimuove gli argomenti passati ad uno script a partire dall’inizio della lista, cioè dal primo argomento. Questo significa che ad ogni ciclo del while non prenderemo il primo argomento, lo controlleremo e poi lo rimuoveremo dall’elenco. Quando si digita due volte shift, si estraggono due parametri.
Viene da se che se l’argomento $1 è, per esempio, uguale a “-d“, allora l’argomento $2 dovrà contenere il nome del dominio e quindi una volta letti li rimuoveremo entrambi.
Se invece l’argomento $1 fosse uguale a “-h” oppure a “–help“, allora visualizzeremmo, senza ulteriori opzioni, una guida allo script. In questo caso dovremmo rimuovere un solo argomento.
Fatto questo cominciamo ad elaborare i parametri acquisiti. Anzitutto controlliamo se sia stato passato un nome utente del database:
1 2 3 4 5 6 7 8 |
call_check_dbadmin() { if [[ $dbadmin = "" ]] then errore "Inserire utente amministratore del database" fi } |
Se non c’è un nome utente del database chiamiamo una funzione che stampi l’errore:
1 2 3 4 5 6 |
errore() { echo -e "${ROSSO}ERRORE!${NC} $1. Per aiuto usare -h o --help" exit } |
In questo caso vogliamo usare, visto che possiamo, anche dei colori, e quindi colorare di rosso la parola “ERRORE!”. Per farlo facciamo riferimento all’ANSI escape code.
Quindi possiamo usare le seguenti combinazioni:
1 2 3 4 5 6 7 8 |
Black 0;30 Dark Gray 1;30 Red 0;31 Light Red 1;31 Green 0;32 Light Green 1;32 Brown/Orange 0;33 Yellow 1;33 Blue 0;34 Light Blue 1;34 Purple 0;35 Light Purple 1;35 Cyan 0;36 Light Cyan 1;36 Light Gray 0;37 White 1;37 |
Questo significa che se nella string “ERRORE! C’è un errore” fosse scritto “\033[0;31mERRORE!\033[0m C’è un errore” la stringa verrebbe colorata tutta di rosso, fino al nuovo colore che è, in questo caso, quello nullo predefinito. Ovvero così:
Per comodità vogliamo parametrizzare alcuni colori inserendo, in cima allo script il seguente codice:
1 2 3 4 |
GIALLO='\033[1;33m' ROSSO='\033[0;31m' CELESTE='\033[1;34m' NC='\033[0m' |
La variabile NC starebbe per No Color, ovviamente i nomi sono a piacere.
Fatto questo controlliamo se sia stata passata anche la password, se non è stato fatto la richiediamo:
1 2 3 4 5 6 7 8 9 10 |
call_check_dbpass() { if [[ $dbadmin != "" && $dbpass = "" ]] then echo -n "$dbadmin password: " read -s dbpass echo fi } |
Potremmo aggiungere altri controlli (per esempio sul nome del dominio, ecc.) ma per ora tralasciamo ed andiamo a creare la cartella del vhost sotto a /var/www.
1 2 3 4 5 6 7 |
www="/var/www/" call_crea_cartella() { mkdir $www$dominio 2>/dev/null || errore "Permesso negato. Eseguire come root" } |
Utilizziamo 2>/dev/null per reindirizzare l’output di un eventuale errore e non mostrarlo a schermo, mentre il connettore || vuol dire che, dati due comandi A e B per cui A || B, se A non va a buon fine, allora esegui B.
Fatto questo creiamo subito l’utente FTP dedicato e la relativa password. In questo esempio do per scontato che abbiamo installato ProFTPd e che sia già stato configurato correttamente sul nostro server:
1 2 3 4 5 6 7 8 9 10 11 |
ftppassword="$(openssl rand -base64 12)" call_crea_ftp() { useradd --shell /bin/false $utente chown $utente:www-data $www$dominio usermod -d $www$dominio $utente echo $utente:$ftppassword | /usr/sbin/chpasswd echo "FTP Password: $ftppassword" } |
Con il primo comando creiamo una password casuale di 12 caratteri.
Creiamo poi un utente chiamato come da argomento e assegniamo, alla cartella creata in precedenza, come proprietario l’utente appena creato e come gruppo www-data (ricordiamoci che sulla cartella deve poter interagire anche Apache). Fatta questa modifica con il comando usermod -d assegniamo la cartella alla home dell’utente. Questo viene fatto per motivi di sicurezza, per cui l’utente che accede al FTP acceda direttamente alla sua “home“, corrispondente anche allo spazio web.
Dopodiché impostiamo la password appena creata come password del nostro utente, ed infine stampiamola a video.
Faccio notare che perché –shell /bin/false non dia problemi, così come la questione della home, è necessario che nel file di configurazione di ProFTPd (/etc/proftpd/proftpd.conf) siano presenti i seguenti parametri così configurati (tipicamente va tolto il cancelletto):
1 2 |
DefaultRoot ~ RequireValidShell off |
Fatto questo scriviamo il nostro file del VirtualHost:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 |
sites="/etc/apache2/sites-available/" call_crea_vhost() { cat << EOF > "${sites}${dominio/./_}.conf" <VirtualHost *:80> ServerName $dominio ServerAdmin webmaster@localhost DocumentRoot $www$dominio ErrorLog \${APACHE_LOG_DIR}/error.log CustomLog \${APACHE_LOG_DIR}/access.log combined </VirtualHost> EOF } |
In questo caso vogliamo creare un file del tipo nomedominio.conf dentro a /etc/apache2/sites-available/, già che ci siamo sostituiamo il . con un _, in modo tale che nomedominio.com diventi nomedominio_com e il nome del file nomedominio_com.conf. Con l’istruzione EOF possiamo scrivere direttamente dentro il file il testo contenuto in mezzo.
Fatto questo ricarichiamo Apache:
1 2 3 4 5 6 |
call_agg_vhost() { a2ensite "${dominio/./_}" 2>/dev/null || errore "Apache non è installato o configurato correttamente." service apache2 reload 2>/dev/null } |
Adesso ci manca solamente il database:
1 2 3 4 5 6 7 8 9 10 11 12 13 |
newpassword="$(openssl rand -base64 12)" call_crea_db() { dbname="${dominio/./_}" dbname="${dbname/-/_}" mysql -u$dbadmin -p$dbpass -e "DROP DATABASE IF EXISTS $dbname" 2>/dev/null mysql -u$dbadmin -p$dbpass -e "DROP USER $dbname@localhost" 2>/dev/null mysql -u$dbadmin -p$dbpass -e "CREATE DATABASE $dbname" 2>/dev/null mysql -u$dbadmin -p$dbpass -e "CREATE USER $dbname@localhost IDENTIFIED BY '$newpassword'" 2>/dev/null mysql -u$dbadmin -p$dbpass -e "GRANT ALL PRIVILEGES ON $dbname.* TO $dbname@localhost" 2>/dev/null } |
Anche in questo caso abbiamo creato una password che assoceremo ad un utente chiamato come il database medesimo e che ha accesso solamente dal server locale.
Infine, per testare il tutto (questo certo non sarebbe uno standard opportuno per un vero hosting) creiamo un file index.php nel VirtualHost appena configurato e inseriamo dentro i seguenti parametri:
1 2 3 4 5 6 7 8 9 10 11 12 |
call_crea_index() { cat << EOF > "${www}$dominio/index.php" <?php echo "<h1>$dominio under construction</h1>"; mysqli_connect("localhost", "$dbname", "$newpassword", "$dbname") or die("Errore database"); ?> EOF } |
Se tutto è andato bene collegandoci al dominio del hosting appena registrato dovremmo veder scritto Nome_Dominio under construction e nessun avviso di Errore database.
Aggiungo anche un’ultima funzione utile per ripristinare il tutto quando si usa il parametro –fix, cancellando quindi il vhost e le relative cartelle:
1 2 3 4 5 6 7 8 9 10 |
call_rimuovi() { if $ripristina then a2dissite "${dominio/./_}" rm -R $www$dominio 2>/dev/null rm "${sites}${dominio/./_}.conf" fi } |
Il codice completo, riorganizzando quanto detto prima, è questo:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 |
#!/bin/bash # CONFIGURAZIONE SISTEMA sites="/etc/apache2/sites-available/" www="/var/www/" # CODICE DA NON MODIFICARE GIALLO='\033[1;33m' ROSSO='\033[0;31m' CELESTE='\033[1;34m' NC='\033[0m' TAB1='\t' TAB2='\t\t' aiuto=false ripristina=false newpassword="$(openssl rand -base64 12)" ftppassword="$(openssl rand -base64 12)" errore() { echo -e "${ROSSO}ERRORE!${NC} $1. Per aiuto usare -h o --help" exit } while [[ $# -gt 0 ]] do parametro="$1" case $parametro in -d|--domain) dominio="$2" shift shift ;; -u|--user) utente="$2" shift shift ;; --dbadmin) dbadmin="$2" shift shift ;; --dbpass|--dbpassword) dbpass="$2" shift shift ;; -h|--help) aiuto=true shift ;; -f|--fix) ripristina=true shift ;; *) shift ;; esac done call_aiuto() { if $aiuto then echo echo -e "${TAB1}${ROSSO}GUIDA ALL'UTILIZZO${NC}" echo echo -e "${TAB1}$0" echo echo -e "${TAB1}${CELESTE}Comando${NC}" echo -e "${TAB1}$0 -d|--domain DomainName -u|--user DomainUser --dbadmin DatabaseRootUser [--dbpass|--dbpassword DatabaseRootPassword] [-f|--fix]" echo echo -e "${TAB1}${CELESTE}Parametri${NC}" echo -e "${TAB1}${GIALLO}DomainName${NC}${TAB2}nome del dominio che vogliamo creare, per esempio: -d miosito.com" echo -e "${TAB1}${GIALLO}DomainUser${NC}${TAB2}nome dell'utente che gestirà il dominio, per esempio: -u mariorossi" echo -e "${TAB1}${GIALLO}DatabaseRootUser${NC}${TAB1}nome dell'utente amministratore del database, tipicamente root" echo -e "${TAB1}${GIALLO}DatabaseRootPassword${NC}${TAB1}Password dell'utente amministratore del database" echo exit fi } call_check_dbadmin() { if [[ $dbadmin = "" ]] then errore "Inserire utente amministratore del database" fi } call_check_dbpass() { if [[ $dbadmin != "" && $dbpass = "" ]] then echo -n "$dbadmin password: " read -s dbpass echo fi } call_crea_cartella() { mkdir $www$dominio 2>/dev/null || errore "Permesso negato. Eseguire come root" } call_crea_ftp() { useradd --shell /bin/false $utente chown $utente:www-data $www$dominio usermod -d $www$dominio $utente echo $utente:$ftppassword | /usr/sbin/chpasswd echo "FTP Password: $ftppassword" } call_crea_vhost() { cat << EOF > "${sites}${dominio/./_}.conf" <VirtualHost *:80> ServerName $dominio ServerAdmin webmaster@localhost DocumentRoot $www$dominio ErrorLog \${APACHE_LOG_DIR}/error.log CustomLog \${APACHE_LOG_DIR}/access.log combined </VirtualHost> EOF } call_agg_vhost() { a2ensite "${dominio/./_}" 2>/dev/null || errore "Apache non è installato o configurato correttamente." service apache2 reload 2>/dev/null } call_crea_db() { dbname="${dominio/./_}" dbname="${dbname/-/_}" mysql -u$dbadmin -p$dbpass -e "DROP DATABASE IF EXISTS $dbname" 2>/dev/null mysql -u$dbadmin -p$dbpass -e "DROP USER $dbname@localhost" 2>/dev/null mysql -u$dbadmin -p$dbpass -e "CREATE DATABASE $dbname" 2>/dev/null mysql -u$dbadmin -p$dbpass -e "CREATE USER $dbname@localhost IDENTIFIED BY '$newpassword'" 2>/dev/null mysql -u$dbadmin -p$dbpass -e "GRANT ALL PRIVILEGES ON $dbname.* TO $dbname@localhost" 2>/dev/null } call_crea_index() { cat << EOF > "${www}$dominio/index.php" <?php echo "<h1>$dominio under construction</h1>"; mysqli_connect("localhost", "$dbname", "$newpassword", "$dbname") or die("Errore database"); ?> EOF } call_rimuovi() { if $ripristina then a2dissite "${dominio/./_}" rm -R $www$dominio 2>/dev/null rm "${sites}${dominio/./_}.conf" fi } call_rimuovi call_aiuto call_check_dbadmin call_check_dbpass call_crea_cartella call_crea_vhost call_agg_vhost call_crea_db call_crea_index call_crea_ftp |
Il codice è stato scritto e testato su Ubuntu Server 16.04, con sopra installati Apache, MySQL, PHP, OpenSSL e ProFTPd.