ISPConfig : Un mode maintenance avec SSL

By RRZEicons (Cc-sa)

Afin de réaliser des maintenances ou migration sur mon serveur qui héberge ~100 sites et qui tourne avec le panel ISPConfig j’ai mis en place un petit mot d’excuse qui dit en substance : « maintenance en cours, merci de repasser plus tard ». C’est mieux que « La connexion a échoué » ou « Can’t Connect to MySQL Server on nian nian nian » non ?. C’était plutôt simple jusqu’à l’arrivé massive du SSL. parce que quand on tente de joindre https://david.mercereau.info ou https://zici.fr et qu’on est redirigé vers la même page de maintenance, avec le SSL ça coince / ça affiche un message d’erreur, normal… hors c’est ce qu’on cherche à éviter.

La configuration de prod en temps normal  :

  • Apache sur le port 80 & 443
  • Let’sEncrypt délivre les certificat SSL
  • Le tout est motorisé par ISPConfig

La configuration de maintenance :

  • Apache sur le port 80 & 443 (toujours)
  • Lighttpd sur le port 81 & 444
  • Iptables redirige le trafic arrivant sur le port 80 vers le port 81 en PREROUTING sauf pour mon IP (ça permet donc de bricoler)

Installation de lighttpd

Apété, et ayé :

aptitude -y install lighttpd
update-rc.d lighttpd remove
mkdir /var/www/maintenance
echo "Serveur en maintenance" > /var/www/maintenance/index.html # Vous pouvez faire une belle page html c'est mieux !

La configuration de lighttpd : /etc/lighttpd/lighttpd.conf

server.modules = (
	"mod_access",
)

server.document-root        = "/var/www/maintenance"
server.upload-dirs          = ( "/var/cache/lighttpd/uploads" )
server.errorlog             = "/var/log/lighttpd/error.log"
server.pid-file             = "/var/run/lighttpd.pid"
server.username             = "www-data"
server.groupname            = "www-data"
server.port                 = 81
server.error-handler-404 = "/index.html"

index-file.names            = ( "index.html", "index.lighttpd.html" )
url.access-deny             = ( "~", ".inc" )
static-file.exclude-extensions = ( ".php", ".pl", ".fcgi" )

include_shell "/usr/share/lighttpd/create-mime.assign.pl"
include_shell "/usr/share/lighttpd/include-conf-enabled.pl"

Récupérer la configuration SSL

J’ai fais un script qui récupère dans configuration apache (généré par ISPConfig chez moi), les chemins vers les certificats SSL Let’sEncrypt et qui (re)génère la conf lighttpd (qui crée le fichier /etc/lighttpd/conf-enabled/maintenance-mode-ssl.conf).

Préparation avant de lancer le script :

pear install config
mkdir /etc/lighttpd/certs/

C’est bon, on peut lancer le script :

<?php
require_once 'Config.php';

# Add include "maintenance-mode-ssl.conf" In lighttpd.conf
$lighttpdConfig='/etc/lighttpd/conf-enabled/maintenance-mode-ssl.conf';
# Content of SSL .pem
$ligttpdSslDir = '/etc/lighttpd/certs/';
# SSL port
$ligttpdSslPort = 444;

$sitesEnableds = scandir('/etc/apache2/sites-enabled/');
$lighttpdConfigContent='';
foreach($sitesEnableds as $sitesEnabled) {
    if ($sitesEnabled != '.' && $sitesEnabled != '..' && $sitesEnabled != '000-ispconfig.vhost' && $sitesEnabled !=  '000-apps.vhost' && $sitesEnabled != '000-default.conf' && $sitesEnabled != '000-ispconfig.conf') {
        $DocumentRoot=null;
        $ServerAlias=null;
        $ServerAliasMultiple=null;
        $ServerName=null;
        $conf = new Config();
        $root = $conf->parseConfig('/etc/apache2/sites-enabled/'.$sitesEnabled, 'apache');
        if (PEAR::isError($root)) {
            echo 'Error reading config: ' . $root->getMessage() . "\n";
            exit(1);
        }
        // Parse du fichier
        $i = 0;
        while ($item = $root->getItem('section', 'VirtualHost', null, null, $i++)) {
            foreach ($item->children as $child) {
                if ($child->name == 'ServerName') {
                    $ServerName = $child->content;
                }
                if ($child->name == 'ServerAlias') {
                    $ServerAliasMultiple=explode(" ", $child->content);
                    foreach ($ServerAliasMultiple as $ServerAliasOne) {
                        if (is_null($ServerAlias)) {
                            $ServerAlias[]=$ServerAliasOne;
                        } elseif (!in_array($ServerAliasOne, $ServerAlias))  {
                            $ServerAlias[]=$ServerAliasOne;
                        }
                    }
                }
                if ($child->name == 'DocumentRoot') {
                    $DocumentRoot = $child->content;
                }
            }
        }
        if ($ServerName != null && $DocumentRoot != null) {
            if (is_file(substr($DocumentRoot, 0, -3).'ssl/'.$ServerName.'-le.key')) {
                $key=file_get_contents(substr($DocumentRoot, 0, -3).'ssl/'.$ServerName.'-le.key');
                $crt=file_get_contents(substr($DocumentRoot, 0, -3).'ssl/'.$ServerName.'-le.crt');
                file_put_contents($ligttpdSslDir.'/'.$ServerName.'.pem', $key.$crt);
                $lighttpdConfigContent.='$HTTP["host"] == "'.$ServerName.'" {
  $SERVER["socket"] == ":'.$ligttpdSslPort.'" {
    ssl.engine = "enable" 
    ssl.pemfile = "'.$ligttpdSslDir.$ServerName.'.pem" 
  }
}
';
                if (is_array($ServerAlias)) {
                    foreach ($ServerAlias as $ServerAliasJustOne) {
                    $lighttpdConfigContent.= '$HTTP["host"] == "'.$ServerAliasJustOne.'" {
  $SERVER["socket"] == ":'.$ligttpdSslPort.'" {
    ssl.engine = "enable" 
    ssl.pemfile = "'.$ligttpdSslDir.$ServerName.'.pem" 
  }
}
';
                    }
                }
            }
        }
    }
}
file_put_contents($lighttpdConfig, $lighttpdConfigContent);
?>

ça doit générer quelque chose comme ça dans le fichier /etc/lighttpd/conf-enabled/maintenance-mode-ssl.conf :

$HTTP["host"] == "zici.fr" {
  $SERVER["socket"] == ":444" {
    ssl.engine = "enable" 
    ssl.pemfile = "/etc/lighttpd/certs/zici.fr.pem" 
  }
}
$HTTP["host"] == "david.mercereau.info" {
  $SERVER["socket"] == ":444" {
    ssl.engine = "enable" 
    ssl.pemfile = "/etc/lighttpd/certs/david.mercereau.info.pem" 
  }
}
$HTTP["host"] == "calcpv.net" {
  $SERVER["socket"] == ":444" {
    ssl.engine = "enable" 
    ssl.pemfile = "/etc/lighttpd/certs/calcpv.net.pem" 
  }
}
[...]

Maintenance On/Off

Pour se mettre en mode maintenance :

# Mon ip : 
IPMAISONDEMOI=X.X.X.X
# Lancer le script ci-dessus qui génère la conf SSL pour lighttpd 
php maintenance-ssl-lighttpd.php
# Démarrage du serveur d'excuse
service lighttpd start
# Redirection du port 80 vers 81 sauf pour mon IP
iptables -A INPUT -p tcp --dport 81 -j ACCEPT
iptables -A INPUT -p tcp --dport 444 -j ACCEPT
iptables -t nat -A PREROUTING \! -s ${IPMAISONDEMOI} -p tcp --dport 80 -j DNAT --to-destination ${IPDUSERVEUR}:81
iptables -t nat -A PREROUTING \! -s ${IPMAISONDEMOI} -p tcp --dport 443 -j DNAT --to-destination ${IPDUSERVEUR}:444
iptables -t nat -A POSTROUTING -j MASQUERADE

Et pour désactiver le mode maintenance :

# Si vous n'avez pas d'autres règles (c'est mal) :
iptables -F
iptables -t nat -F
# Sinon redémarrer votre service iptables 
# Lighttpd n'est plus utile
service lighttpd stop

Firewall : Mon script iptables

Je partage ici mon script de firewall iptable. C’est un script « à l’ancienne », dans du bash… ça fait le taf, mais rien de bien transsudant. En gros :

  • On ferme tout les ports sauf ceux qui nous intéresse (80, 25, icmp…)
  • Petite fonction pour ouvrir les ports mis en écoute sur Portsentry. Portsentry c’est un petit logiciel de sécurité en mode « pot de miel ». On met des ports en écoute mais il n’y a rien derrière. Dès que quelqu’un tente de s’y connecter (un robot ou quelqu’un de malveillant), ça bloque son IP dans le firewall pour un temps donnée. C’est radical si vous déplacez le port SSH du 22 vers autre chose et que vous mettez Portsentry à écouter (entre autre) sur le 22…
  • Mode maintenance du serveur web (lancé via ./iptables.sh maintenance). Il permet de mettre une page de maintenance pour tout le monde sauf pour vous (j’explique en détail dans cet article)
#!/bin/bash

## IP :
# Chez moi
MOI="A.A.A.A" 
# Mon serveur
SRV1="X.X.X.X"

IPT="/sbin/iptables"
PORTSENTRYCONF="/etc/portsentry/portsentry.conf"

export IPT PORTSENTRYCONF

function portsentryOpen() {
	. ${PORTSENTRYCONF}
	IFS=',' read -ra TCP_PORTS_SPLIT <<< "${TCP_PORTS}"
	for TCP_PORT in "${TCP_PORTS_SPLIT[@]}"; do 
		${IPT} -A INPUT -p tcp --dport ${TCP_PORT} -j ACCEPT
	done
	IFS=',' read -ra UDP_PORTS_SPLIT <<< "${UDP_PORTS}"
	for UDP_PORT in "${UDP_PORTS_SPLIT[@]}"; do 
		${IPT} -A INPUT -p udp --dport ${UDP_PORT} -j ACCEPT
	done
}

# Remise a 0
${IPT} -F
${IPT} -t nat -F

# Les connexions entrantes sont bloquées par défaut
${IPT} -P INPUT DROP
# Les connexions destinées à être routées sont acceptées par défaut
${IPT} -P FORWARD ACCEPT
# Les connexions sortantes sont acceptées par défaut
${IPT} -P OUTPUT ACCEPT


######################
# Règles de filtrage #
######################
# Nous précisons ici des règles spécifiques pour les paquets vérifiant
# certaines conditions.
 
# Pas de filtrage sur l'interface de "loopback"
${IPT} -A INPUT -i lo -j ACCEPT
 
# Accepter le protocole ICMP (notamment le ping)
${IPT} -A INPUT -p icmp -j ACCEPT
  
# Accepter les packets entrants relatifs à des connexions déjà
# établies : cela va plus vite que de devoir réexaminer toutes
# les règles pour chaque paquet.
${IPT} -A INPUT -m state --state RELATED,ESTABLISHED -j ACCEPT

# ftp 
${IPT} -A INPUT -p tcp --dport 20 -j ACCEPT 
${IPT} -A INPUT -p tcp --dport 21 -j ACCEPT
# Préalabielemnt, pour pure-ftpd : echo "29700 29750" > /etc/pure-ftpd/conf/PassivePortRange ${IPT} -A INPUT -p tcp --dport 29700:29750 -j ACCEPT
# SSH
${IPT} -A INPUT -p tcp --dport 222 -j ACCEPT
# NTP
${IPT} -A INPUT -p udp --dport 123 -j ACCEPT
# smtp
${IPT} -A INPUT -p tcp --dport smtp -j ACCEPT
# Pour test bricolage smtp
${IPT} -A INPUT -p tcp --dport 587 -j ACCEPT
# imap(s)
${IPT} -A INPUT -p tcp --dport 143 -j ACCEPT
${IPT} -A INPUT -p tcp --dport 993 -j ACCEPT
# sieve
${IPT} -A INPUT -p tcp --dport 4190 -j ACCEPT
# dns
${IPT} -A INPUT -p tcp --dport domain -j ACCEPT
${IPT} -A INPUT -p udp --dport domain -j ACCEPT
# http
${IPT} -A INPUT -p tcp --dport http -j ACCEPT
# https
${IPT} -A INPUT -p tcp --dport https -j ACCEPT

# Maintenance 
if [ "$1" == "maintenance" ] ; then
	echo "Maintenance On"
	/usr/sbin/service lighttpd start
	${IPT} -A INPUT -p tcp --dport 81 -j ACCEPT
	${IPT} -t nat -A PREROUTING \! -s ${MOI} -p tcp --dport 80 -j DNAT --to-destination ${SRV1}:81
	${IPT} -t nat -A POSTROUTING -j MASQUERADE
elif [ -f "/var/run/lighttpd.pid" ] ; then
	echo "Maintenance Off"
	/usr/sbin/service lighttpd stop
fi

# Portsentry 
if [ -f ${PORTSENTRYCONF} ] ; then
	portsentryOpen ${IPT} ${PORTSENTRYCONF}
fi

# End
${IPT} -A INPUT -j LOG --log-prefix "iptables denied: "  --log-level 4
${IPT} -A INPUT -j REJECT

# Si vous utilisez fail2ban, relancé à la fin du script :
#/usr/sbin/service fail2ban restart

 

 

 

RaspberryPi & Raspbian en lecture seul (ReadOnly) pour préserver la carte SD

Le Raspberry Pi, est un mini ordinateur qui consomme très peu d’énergie. Il n’y a pas de disque dur mécanique, le système se trouve sur une carte SD.  L’avantage c’est que ça consomme moins d’énergie mais la carte SD à l’inconvénient de s’abîmer très rapidement quand il y a beaucoup de lecture/écriture (et sa durée de vie n’en ai que moindre). J’ai donc passé mon Raspberry Pi sous Raspbian (une Debian pré-packagé pour Raspberry) et mis en place un système en lecture seul. Il s’agit ici d’une installation type serveur sans interface graphique.

Installation de Raspbian (sans écran sur le Raspberry) avec connexion Wifi

Vue que je n’ai pas d’écran pour installer mon Raspberry, j’ai mis la carte SD dans mon ordinateur portable pour l’installation. Après le téléchargement de « Raspbien lite » sur le site officiel : http://www.raspbian.org. Il suffit d’utiliser la commande dd pour installer l’image :

david@portabuntu:~/Téléchargements$ unip raspbian_lite_latest.zip
david@portabuntu:~/Téléchargements$ sudo dd bs=4M if=2016-05-10-raspbian-jessie-lite.img of=/dev/sdc
330+1 enregistrements lus
330+1 enregistrements écrits
1386217472 octets (1,4 GB) copiés, 86,4596 s, 16,0 MB/s

Attention : remplacer /dev/sdc par le périphérique de votre carte SD ! (/dev/sdb, /dev/mmcblk0… un « sudo fdisk  -l » pourra vous en dire plus)

Éjecter la carte SD et remettez là, vous devriez avoir plusieurs partition sur la carte SD :

  • #1 : FAT32 (partition de boot)
  • #2 : ext3 (système)

Utilisation de gparted pour agrandir l’espace disque de la partition système :

Attention : cet article est vieux, préféré celui-ci pour la partie wifi : https://www.inpact-hardware.com/article/270/100830-comment-installer-simplement-raspberry-pi-sous-raspbian-avec-ssh-et-wi-fi-sans-ecran

On va maintenant préparer la connexion Wifi pour pouvoir l’attaquer en SSH :

sudo mkdir /mnt/sd-sys
sudo mount /dev/sdc2 /mnt/sd-sys # (la partition ext3)
sudo vi /mnt/sd-sys/etc/network/interfaces

L’édition de se fichier interface qui gère les cartes réseaux :

< iface wlan0 inet manual
< wpa-conf /etc/wpa_supplicant/wpa_supplicant.conf
---
> auto wlan0
> iface wlan0 inet static
> address 192.168.1.2
> netmask 255.255.255.0
> gateway 192.168.1.1
> wpa-ssid "VOTRE SSID WIFI"
> wpa-psk "VOTRE CLEF WAP PSK"

On spécifie le serveur DNS en modifiant le fichier /mnt/sd-sys/etc/network/interfaces

< #name_servers=127.0.0.1
---
> name_servers=192.168.1.1

Bien sûr il faut mette des IP’s de votre réseau…

On éjecte la carte :

david@portabuntu:~$ sudo umount /dev/sdc2 
david@portabuntu:~$ sudo eject /dev/sdc

On met la carte SD dans le Raspberry et on l’allume, on partiente que la connexion au Wifi soit faite et on test la connexion ssh :

david@portabuntu:~$ ssh pi@192.168.1.2 
The authenticity of host '192.168.1.2 (192.168.1.2)' can't be established.
ECDSA key fingerprint is fe:ed:f6:fe:e5:ea:28:bb:ad:6d:0c:2e:8f:b1:2c:5b.
Are you sure you want to continue connecting (yes/no)? yes
Warning: Permanently added '192.168.1.2' (ECDSA) to the list of known hosts.
pi@192.168.1.2's password: 

The programs included with the Debian GNU/Linux system are free software;
the exact distribution terms for each program are described in the
individual files in /usr/share/doc/*/copyright.

Debian GNU/Linux comes with ABSOLUTELY NO WARRANTY, to the extent
permitted by applicable law.
pi@raspberrypi:~ $ 

ça fonctionne !!!!

Passage du système en ReadOnly

Je me suis bien aidé des différents sites suivant :

Le reste des commandes va s’effectuer avec les droits root :

pi@raspberrypi:~ $ sudo -i
root@raspberrypi:~#

Il va falloir minimiser les programmes qui écrivent sur le FileSystème. On commence par désactiver la SWAP :

dphys-swapfile swapoff
dphys-swapfile uninstall
update-rc.d dphys-swapfile disable

Et on fait du ménage :

apt-get remove --purge logrotate dbus dphys-swapfile  fake-hwclock

Sauf si vous utilisez le DHCP, dans ce cas il faudra ajouter des choses pour que ça fonctionne en RO (« ln -s /tmp /var/lib/dhcp » par exemple…) sinon on supprime aussi le client DHCP :

aptitude purge isc-dhcp-client  dhcpcd5  isc-dhcp-common

On met l’horloge sur le bon fuseau horaire (Europe/Paris pour moi) :

rm /etc/localtime
ln -s /usr/share/zoneinfo/Europe/Paris /etc/localtime 

On remplace le « log manager » rsyslogd par busybox one qui fonctionne en RAM et on en FS :

apt-get install busybox-syslogd; dpkg --purge rsyslog

Pour lire les logs il faut utiliser la commande logread (logread -f correspond à un tail -f sur le syslog)

Encore un peu de ménage au démarrage :

systemctl disable bootlogs
systemctl disable sudo # Si vous n'utilisez pas sudo
systemctl disable alsa-utils # Si vous n'utilisez pas le son
systemctl disable console-setup
systemctl disable fake-hwclock # Certainement déjà absent à ce stade

A ce stade je conseil d’installer les petits outils indispensables

On désactive le bash_history soit en supprimant complètement le fichier

history -c
rm ~/.bash_history -rf
export HISTFILESIZE=0
unset HISTFILE
echo "HISTFILESIZE=0" >> ~/.bashrc

Soit en le déplaçant dans /tmp. Il sera remis à 0 à chaque reboot mais fonctionnera en read only.

+ HISTFILE="/tmp/${USER}_bash_history"

Avant de mettre le système en read only on va faire deux alias pour switcher du mode read-only on mode read-write facilement. Ajouter dans bashrc commun : /etc/bash.bashrc :

# Fonction pour connaître le mode en cours
fs_mode=$(mount | sed -n -e "s/^.* on \/ .*(\(r[w|o]\).*/\1/p")
# alias ro/rw pour passer de l'un à l'autre
alias ro='mount -o remount,ro / ; fs_mode=$(mount | sed -n -e "s/^.* on \/ .*(\(r[w|o]\).*/\1/p")'
alias rw='mount -o remount,rw / ; fs_mode=$(mount | sed -n -e "s/^.* on \/ .*(\(r[w|o]\).*/\1/p")'
# Modification du prompt pour afficher le mode en cours
export PS1='\[\033[01;32m\]\u@\h${fs_mode:+($fs_mode)}\[\033[00m\]:\[\033[01;34m\]\w\[\033[00m\]\$ '

Aller hop, on passe au chose sérieuse, on modifie le /etc/fstab :

< /dev/mmcblk0p1  /boot           vfat    defaults          0       2
< /dev/mmcblk0p2  /               ext4    defaults,noatime  0       1
---
> /dev/mmcblk0p1  /boot           vfat    defaults,ro          0       2
> /dev/mmcblk0p2  /               ext4    defaults,noatime,ro  0       1
> tmpfs	/var/log	tmpfs	nodev,nosuid	0	0
> tmpfs	/var/tmp	tmpfs	nodev,nosuid	0	0
> tmpfs	/tmp	tmpfs	nodev,nosuid	0	0

Puis le fichier /boot/cmdline.txt :

< dwc_otg.lpm_enable=0 console=serial0,115200 console=tty1 root=/dev/mmcblk0p2 rootfstype=ext4 elevator=deadline fsck.repair=yes rootwait
---
> dwc_otg.lpm_enable=0 console=serial0,115200 console=tty1 root=/dev/mmcblk0p2 rootfstype=ext4 elevator=deadline fsck.repair=yes rootwait noswap ro

Après un reboot on peut tester :

root@raspberrypi(ro):~# touch /root/testEcriture
touch: cannot touch ‘/root/testEcriture’: Read-only file system
root@raspberrypi(ro):~# rw
root@raspberrypi(rw):~# touch /root/testEcriture
root@raspberrypi(rw):~# rm /root/testEcriture
root@raspberrypi(rw):~# ro
root@raspberrypi(ro):~#

ça fonctionne !

Le petit plus du chef, un petit script ~/.bash_logout pour ne pas oublier de remettre le FS en read only après avoir travaillé dessus…

#!/bin/bash
if [ "$fs_mode" != "ro" ]; then
	read -p "Le FS est en lecture/écriture, ne voudriez vous pas le basculer en lecture seul ? [O/n] " 
	if [[ ! $REPLY =~ ^[Nn]$ ]]
	then
		echo "Bascule en Read/Only"
		ro
	else
		echo "Ok on fait rien tant pi... Mais n'oublie pas que ça use la carte SD :-/"
	fi
fi

[API OVH] Reboot d’un VPS via un script PHP

OVH possède une API qui peut s’avérer très pratique pour automatiser des tâches du manager : https://api.ovh.com

J’ai choisi d’utiliser le PHP pour ces scripts car il est déjà installé sur mes machines, je n’ai donc pas besoin de supplément.

Premier test, lister mes VPS

Je commence gentils, je fais un script qui listera simplement les VPS disponible chez OVH.

Télécharger et décompressé la dernière version de « php-ovh » et ces dépendances : https://github.com/ovh/php-ovh/releases

root@monsrv:~# mkdir ovhphp/
root@monsrv:~# cd ovhphp/
root@monsrv:~/ovhphp# wget https://github.com/ovh/php-ovh/releases/download/v2.0.0/php-ovh-2.0.0-with-dependencies.tar.gz
root@monsrv:~/ovhphp# tar -xzvf php-ovh-2.0.0-with-dependencies.tar.gz 

Ensuite on crée un tocken via cette URL : https://api.ovh.com/createToken/?GET=/vps ou on ne va autoriser que le GET sur /vps.

On va obtenir la page suivante, il va falloir conserver Application Key, Application Secret & Consumer Key pour les mettre dans le script :

OVH api keys résulstatOn va pouvoir créer le script suivant (pensez à recopier vos Application Key, Application Secret & Consumer Key dans les variables) :

#!/usr/bin/php
<?php

// This script is a skeleton of script or application using OVH APIs
// You can launch it with the command php script.php
// Or include it into a website be deleting the first line

require __DIR__ . '/vendor/autoload.php';
use \Ovh\Api;

////////////////////////////////////////////////////
//     Dont forget to update your credentials     //
////////////////////////////////////////////////////

// Please read the documentation to get your credentials
// https://github.com/ovh/php-ovh
$applicationKey = "hsNT*************";
$applicationSecret = "8FcOC***********************";
$consumer_key = "bU5j**************************";

// Information about API and rights asked
$endpoint = 'ovh-eu';

// Get API connector instance
$conn = new Api(    $applicationKey,
                    $applicationSecret,
                    $endpoint,
                    $consumer_key);

////////////////////////////////////////////////////
//       Your logic will be inserted here         //
////////////////////////////////////////////////////

// This is an example
// Here, use the API connector as you want. Here, we are getting all hosting services
$vps = $conn->get('/vps');

print_r($vps);

?>

Lancement du script, voici le résultat :

root@romeo:~/ovhphp# php getvps.php 
Array
(
    [0] => vks00000.ip-XX-XX-XX.eu
)

vks00000.ip-XX-XX-XX.eu c’est le nom de mon VPS (pour l’exemple), ça fonctionne.

Les choses sérieuses :  un reboot

Même script avec action cette fois, pour tester le reboot d’un VPS via un script.

De nouveau la création d’API  avec l’url https://api.ovh.com/createToken/?POST=/vps/vks00000.ip-XX-XX-XX.eu/reboot. Ici on va autoriser le POST sur /vps/vks00000.ip-XX-XX-XX.eu/reboot.

#!/usr/bin/php
<?php
require __DIR__ . '/vendor/autoload.php';
use \Ovh\Api;
$applicationKey = "fCj********";
$applicationSecret = "vqm**************";
$consumer_key = "Zja************************";
$endpoint = 'ovh-eu';
$conn = new Api(    $applicationKey,
                    $applicationSecret,
                    $endpoint,
                    $consumer_key);
$vps_reboot = $conn->post('/vps/vks00000.ip-XX-XX-XX.eu/reboot');
print_r($vps_reboot);
?>

Au lancement on obtient le numéro de la tâche :

root@romeo:~# php postrebootvps.php
Array
(
    [progress] => 0
    [id] => 5119979
    [type] => rebootVm
    [state] => todo
)

Et ça fait bien redémarrer le VPS…

La finalité

De temps en temps mon VPS est inaccessible. C’est très variable (1 à 2 fois par mois environ) et je ne trouve pas la cause (OVH / système  / attaque ?). La seul solution pour le faire repartir c’est un reboot via le manager OVH. En attendant de trouver la cause (je ne désespère pas) j’ai mis en place ce script sur un serveur tiers afin de déclencher un reboot si le serveur est inaccessible.

#!/usr/bin/php
<?php

// Version 0.1 15/03/2015
// Reboot d'un VPS OVH (via l'API OVH) si celui-ci ne répond pas
// Par David Mercereau : http://david.mercereau.info
// Script sous licence BEERWARE

require __DIR__ . '/vendor/autoload.php';
use \Ovh\Api;
    
// Identifiant API OVH : https://api.ovh.com/createToken/
$applicationKey = "fCj********";
$applicationSecret = "vqm*************";
$consumer_key = "Zja***********************";
$endpoint = 'ovh-eu';
// Host à monitorer (IP du VPS)
$serveurMonitorIp = 'A.B.C.D';
// Port à monitorer (doivent être ouvert sur le VPS)
$serveurMonitorPorts = array(80, 22, 25);;
// Site qui permettent de vérifier la connexion internet
$hostsCheck = array('www.wordpress.com', 'fr.wikipedia.org', 'www.ovh.com');

function checkInternet($hostsCheck) {
    $return = true;
    foreach ($hostsCheck as $hostCheck) {
        if (!$sock = @fsockopen($hostCheck, 80, $num, $error, 5)) {
            echo "CheckInternet : Le serveur $hostCheck ne répond pas sur le port 80. Il n'y a pas internet ?\n";
            $return = false;
        }
    }
    return $return;
}

function serveurMonitor($serveurMonitorIp, $serveurMonitorPorts) {
    $return = true;
    foreach ($serveurMonitorPorts as $serveurMonitorPort) {
        if (!$sock = @fsockopen($serveurMonitorIp, $serveurMonitorPort, $num, $error, 5)) {
            echo "ServeurMonitor: Le serveur $serveurMonitorIp ne répond pas sur le port $serveurMonitorPort, Il n'est H.S. ?\n";
            $return = false;
        }
    }
    return $return;
}

if (! serveurMonitor($serveurMonitorIp, $serveurMonitorPorts)) {
    echo "...second test dans 2 minutes, c'est peut être temporaire...\n";
    sleep(120);
    if (! serveurMonitor($serveurMonitorIp, $serveurMonitorPorts)) {
        if  (checkInternet($hostsCheck)) {
            echo "Reboot de $serveurMonitorIp\n";
            // Conneixion à l'API
            $conn = new Api(    $applicationKey,
                                $applicationSecret,
                                $endpoint,
                                $consumer_key);
            $vps_reboot = $conn->post('/vps/vks10057.ip-37-59-126.eu/reboot');
            print_r($vps_reboot);
        } else {
            echo "Le serveur est peut être planté mais peut être pas... il ne semble pas y a voir interne, on fait rien !\n";
        }
    }
} 
exit(0);
?>

En tâche planifiée :

15,45 * * * *  /root/ovhphp/check-serveur.php

 

Horde upgrade (avec pear standalone)

Cet article fait suite à l’article sur l’installation de Horde en standalone via pear.

Pour faire un upgrade d’horde on lance deux commande :

/var/www/horde/pear/pear -c /var/www/horde/pear.conf channel-update pecl.php.net
/var/www/horde/pear/pear -c /var/www/horde/pear.conf upgrade-all

Ensuite on va prendre un thé.

L’upgrade écrase le .htaccess où nous avions indiqué l’emplacement de nos libs pear. Il faut donc le re-éditer /var/www/horde/.htaccess

[...]
# Spécifie l'emplacement de pear
php_value include_path /var/www/horde/pear/php
SetEnv PHP_PEAR_SYSCONF_DIR /var/www/horde

Ensuite il faut se connecter à horde en admin (http://horde.chezvous.com/admin/config) et lancer la mise à jours des configurations et des schémas de bases (suivre la procédure à l’écran, c’est du clic clic…).

Si tout c’est bien passé vous devriez avoir un panel d’admin dans ce goût là :

Terminé !

Déploiement de poste : FOG & Drivers windows

Drivers-install.vbsFOG est un outil formidable pour le déploiement de poste client que j’utilise en milieu professionnel. J’aurais envie de dire qu’il ne lui manque que la parole, mais non : il ne lui manque que la gestion des drivers

Devant un parc d’ordinateur plutôt hétérogène je ne pouvais pas mettre tous les drivers dans mon image. Je les donc déporté sur un partage réseau et j’ai utilisé DPInst (du Windows Driver Kit) dans un petit script VBS maison. Ce script est lancé automatiquement après le déploiement du poste grâce à sysprep.

Scénario du script :

  1. Monte \\monserveur.mondomaine.lan\drivers en lecteur réseau (partage en lecture seul avec simplement les drivers
  2. Recherche le répertoire du même nom que le “ComputerSystem Model”, que vous pouvez obtenir avec la commande « wmic computersystem get model » (ex : OptiPlex 380)
  3. Copy les drivers en local (car en cas d’installation des drivers de la carte réseau ça coupe…)
  4. Lance de “dpinst.exe” sur le répertoire local
  5. Démonte le lecteur réseau

Noter qu’il faut au minimum que la carte réseau soit reconnu par Windows pour que le script fonctionne.

' David Mercereau - Institut Saint-Dominique de saint-herblain Juin 2014
' Post-déploiement - installation des drivers selon le modèle de machine

''''''''' Déclaration 

On Error Resume Next

Dim objNet, objFso, objLogFile, objShell
Dim tmpObj, tmpItem
Dim SystemName, MakeModel, LocalisationDrivers, LecteurReseau

SystemName = "localhost"
' La localisation des drivers - ne doit pas nécessité d'authentification réseau.-
LocalisationDrivers = "\\monserveur.mondomaine.lan\drivers"
RepertoireTravailLocal = "C:\Windows\DeploiementDrivers"
LecteurReseau = "M"

Set objNet   = CreateObject("Wscript.Network")
Set objFso   = CreateObject("Scripting.FileSystemObject")
Set objShell = CreateObject("WScript.Shell")

''''''''' Script 

' On monte le lecteur réseau
objNet.MapNetworkDrive LecteurReseau & ":", LocalisationDrivers

' On recherche le modèle 
set tmpObj = GetObject("winmgmts:{impersonationLevel=impersonate}!\\" & SystemName & "\root\cimv2").InstancesOf ("Win32_ComputerSystem")
for each tmpItem in tmpObj
	' MakeModel = trim(tmpItem.Manufacturer) & "-" & trim(tmpItem.Model)
	MakeModel = trim(tmpItem.Model)
next
Set tmpObj = Nothing: Set tmpItem = Nothing

If (objFso.FolderExists(LecteurReseau & ":\" & MakeModel)) Then
	' Création du répertoire local
	objFso.CreateFolder RepertoireTravailLocal
	' Copie de dpinst.exe & dpinst.xml
	objShell.Run "cmd /C copy " & LecteurReseau & ":\dpinst* " & RepertoireTravailLocal, 1, true
	' Synchro du répertoire de drivers
	objShell.Run "cmd /C robocopy /S """ & LecteurReseau & ":\" & MakeModel & """ " & RepertoireTravailLocal, 1, true
	' Lancement de l'installation
	objShell.Run "cmd /C c: & cd " & RepertoireTravailLocal & " & dpinst.exe /c", 1, true
	Set objShell = Nothing 
End if

' On démonte le lecteur réseau
objNet.RemoveNetworkDrive LecteurReseau & ":", true, true

Et ça fonctionne au petit oignon !

Liens utiles :

Service web : Un mode maintenance pour bricoler

2ème version de ce « truc », avec support SSL, à découvrir par là : Un mode maintenance avec SSL

By RRZEicons (Cc-sa)
By RRZEicons (Cc-sa)

Afin de réaliser des maintenances sur mon service web (ou les services attenant tel que Mysql) j’ai mis en place un petit mot d’excuse qui dit en substance : « maintenance en cours, merci de repasser plus tard ». C’est mieux que « La connexion a échoué » ou « Can’t Connect to MySQL Server on nian nian nian » non ?

La configuration de prod  :

  • Apache sur le port 80

La configuration de maintenance :

  • Apache sur le port 80 (le même)
  • Lighttpd sur le port 81
  • Iptables redirige le trafic arrivant sur le port 80 vers le port 81 en PREROUTING sauf pour mon IP (ça permet donc de bricoler)

Installation de lighttpd

Apeuté, et ayé :

aptitude -y install lighttpd
update-rc.d lighttpd remove
mkdir /var/www/maintenance
echo "Serveur en maintenance" > /var/www/maintenance/index.html # Vous pouvez faire une belle page html c'est mieux !

Configuration de lighttpd

Éditer le fichier /etc/lighttpd/lighttpd.conf

server.modules = (
	"mod_access",
)

server.document-root        = "/var/www/maintenance"
server.upload-dirs          = ( "/var/cache/lighttpd/uploads" )
server.errorlog             = "/var/log/lighttpd/error.log"
server.pid-file             = "/var/run/lighttpd.pid"
server.username             = "www-data"
server.groupname            = "www-data"
server.port                 = 81
server.error-handler-404    = "/index.html"

index-file.names            = ( "index.html", "index.lighttpd.html" )

include_shell "/usr/share/lighttpd/create-mime.assign.pl"

Il est certainement possible de faire plus propre sur ce point. Actuellement je gère le message dans le index.html que je met dans le error404. Des redirection 302 quelque soit l’URL serait à envisager.

Maintenance On/Off

Pour se mettre en mode maintenance :

# Démarrage du serveur d'excuse
service lighttpd start
# Redirection du port 80 vers 81 sauf pour mon IP
iptables -A INPUT -p tcp --dport 81 -j ACCEPT
iptables -t nat -A PREROUTING \! -s ${IPMAISONDEMOI} -p tcp --dport 80 -j DNAT --to-destination ${IPDUSERVEUR}:81
iptables -t nat -A POSTROUTING -j MASQUERADE

Et pour désactiver le mode maintenance :

# Si vous n'avez pas d'autres règles (c'est mal) :
iptables -F
iptables -t nat -F
# Sinon redémarrer votre service iptables 
# Lighttpd n'est plus utile
service lighttpd stop

Si vous avez des suggestions…

En continuant à utiliser le site, vous acceptez l’utilisation des cookies (au chocolat) Plus d’informations

Les cookies sont utilisés à des fin de statistique de visite du blog sur une plateforme indépendante que j'héberge moi même. Les statistiques sot faites avec un logiciel libre. Aucune information n'est redistribué à google ou autre. Je suis seul autorisé à lire ces informations

Fermer