UFDBGuard : Time-Based ACLs et Default ACL

Ajouter une règle de plage horaire

UFDBGuard propose une gestion des horaires (voir page 36 de leur manuel).

On peut, grace à cette ACL, appliquer (ou non) des politique de blocage/ouverture sur des catégories de domaines/Expressions…

Par exemple : bloquer le sport et les sites de rencontre  aux heures de bureau mais les ouvrir en dehors.

La doc est très claire quant on utilise des ACL multiples mais mais ne signale pas que ce type d’ ACL n’est pas compatible avec l’ACL défaut.

 

Exemple d’ACL ne fonctionnant pas

 


time free-time {
weekly * 12:00-13:30
}


# define web content access rights
acl {

default outside free-time {
pass !porn !sport !dating any
}else{
pass !porn any
}
}

Un coup d’oeil dans les log vous montrera que UFDBGuard est en « FATAL ERROR » à chaque fois qu’il est sollicité.

Il semblerait qu’il soit impossible d’associer une ACL Time à Default. Bien dommage quant on n’a pas besoin d’avoir une policy (basée sur IP, groupe…) .

On va donc devoir créer une policy (qui soit toujours vraix) pour court-circuiter default.

Pour ma part j’ai créer une policy sur ma plage IP (car je sais qu’elle sera toujours vrai).
Je suis sur une plage 172.16.x.x


time free-time {
weekly * 12:00-13:30
}

src blacklistsrc {
ip 172.16.0.0/16
}

# define web content access rights
acl {
blacklistsrc outside free-time
{
pass !porn !sport !dating any
}
else
{
pass !porn any
}
default {
pass any
}
}

Avec une ACL « non default » tout fonctionne correctement.

Plusieurs plages horaire de pause ?


time free-time {
weekly *        12:00-13:30
weekly *        16:00-16:30
weekly thu,fri  11:30-12:00
}

 

 

DNS dynamique avec Debian

Pour obtenir un DNS dynamique il faut 2 choses :

  • Se déclarer auprès d’un service de DNS Dynamique
  • Installer un demon sur son serveur

 

Le service

J’ai opté pour freedns.afraid.org  .

Créez un compte et ajouter un sous-domaine (rien de compliqué)

Le démon

apt-get install inadyn

inadyn supporte de nombreux services :

  • http://www.dyndns.org
  • http://freedns.afraid.org
  • http://zoneedit.com
  • http://www.no-ip.com
  • http://www.easydns.com
  • http://www.tzo.com
  • http://www.3322.org
  • http://www.dnsomatic.com
  • http://www.tunnelbroker.net
  • http://dns.he.net/
  • http://www.dynsip.org
  • http://www.sitelutions.com
  • http://www.dnsexit.com
  • http://www.changeip.com

Le paramétrer (/etc/inadyn.conf)


# Service provider
# Please see inadyn(8) for a complete list of providers
system default@freedns.afraid.org


# Your username
username monusername

# Your password
password monpassword

# Your hostname. This option can appear multiple times
alias monsousdomaine.mooo.com

A par le « system » rien à expliquer.

Pour system vous trouverez dans le man la valeur correspondant à votre service.

Reste à passer inadyn en demon (/etc/default/inadyn)


...

# Set to "yes" if inadyn should run in daemon mode
# If this is changed to "yes", RUN_IPUP must be set to "no".
RUN_DAEMON="yes"

...

service inadyn restart

service inadyn status   -> pour véirfier que le service a bien démarré

Plus qu’à suivre les logs pour valider (update toute les 300s par défaut) que le service update bien votre IP.

tail -f /var/log/inadyn/inadyn.log


Tue Dec 29 16:39:26 2015: Will retry again in 300 sec...
Tue Dec 29 16:44:26 2015: .
Tue Dec 29 16:44:27 2015: Checking for IP# change, connecting to checkip.dyndns.org(91.198.22.70)
Tue Dec 29 16:44:35 2015: No IP# change detected, still at *********
Tue Dec 29 16:49:35 2015: .
Tue Dec 29 16:49:35 2015: Checking for IP# change, connecting to checkip.dyndns.org(216.146.43.70)
Tue Dec 29 16:49:43 2015: No IP# change detected, still at *********
Tue Dec 29 16:54:43 2015: .
Tue Dec 29 16:54:43 2015: Checking for IP# change, connecting to checkip.dyndns.org(91.198.22.70)
Tue Dec 29 16:54:59 2015: No IP# change detected, still at **********

 

 

 

 

 

 

Accéder à SSH via HTTPS (port 443)

Le grand classique : votre client est OK pour exposer le port 443 car il en a besoin pour accéder à sa WebApp mais impossible de vous ouvrir le port 22 (raisons multiples : « on peut pas », « SSH c’est quoi ? » …) pour accéder au serveur.

Grâce à ShellInABox et au mode proxy d’Apache l’on va pouvoir « mapper » un « SSH Web » à une URL de votre site Web et ainsi accéder en SSH – via HTTPS –  au serveur via un simple navigateur web .

Source image : https://github.com/shellinabox/shellinabox


Maquette (Debian)

Activation des modules Apache de gestion du SSL et Proxy


a2enmod ssl
a2enmod proxy
a2enmod proxy_http

Sur Debian (en tout cas sur la Raspbian utilisé pour ce test) il existe déjà un fichier de configuration valide (avec un certificat auto-signé).

/etc/apache2/sites-available/default-ssl.conf

...
# SSL Engine Switch:
# Enable/Disable SSL for this virtual host.
SSLEngine on
# A self-signed (snakeoil) certificate can be created by installing
# the ssl-cert package. See
# /usr/share/doc/apache2/README.Debian.gz for more info.
# If both key and certificate are stored in the same file, only the
# SSLCertificateFile directive is needed.
SSLCertificateFile /etc/ssl/certs/ssl-cert-snakeoil.pem
SSLCertificateKeyFile /etc/ssl/private/ssl-cert-snakeoil.key

...

On active ce host


a2ensite default-ssl

 

On ajoute le « mapping » entre l’URL /shell et ShellInABox

[Avant] /etc/apache2/sites-available/default-ssl.conf


        </VirtualHost>

</IfModule>

 

[Après] /etc/apache2/sites-available/default-ssl.conf


        </VirtualHost>

<Location /shell>
ProxyPass http://localhost:4200/
</Location>
</IfModule>

 

On re-démarre les services


service shellinabox restart

service apache2 restart

 

Vous pouvez à présent accéder à votre SSH via l’adresse https://[votreIP]/shell


Attention : ShelInABox est encore en écoute sur son port. Pour limiter l’accès au seul port 443 il faut modifier la configuration de ShellInABox afin  de limiter son écoute au localhost.

/etc/default/shellinabox


...
SHELLINABOX_ARGS="--localhost-only"

...

Stress test/benchmaking rapide d’un serveur web avec Siege.

Siege est un petit outil permettant de réaliser des stress test rapide (ainsi qu’un benchmarking)  et non qualitatifs de serveurs web.

J’utilise Siege actuellement pour benchmaker mon RaspberryPI Zero avec Raspbian Jessie /Apache.

Pour installer Siege sous Debian/Ubuntu


apt-get install siege

 

Lancer le test :


siege -d10 -c30 -r1000 -v http://lderpib1.local/i2.html

 

  • -d : délais en seconde entre chaque appel de page (par utilisateur)
  • -c : nombre d’utilisateurs (thread) attaquant le serveur web
  • -r : nombre de répétitions. Vous pouvez indiquer un nombre énorme. Un simple ctl+c stoppe le test et vous génère le rapport de benchmark.
  • -v : affiche en console les temps de chaque transaction.

HTTP/1.1 200   0.86 secs:   12360 bytes ==> GET  /i2.html
HTTP/1.1 200   0.52 secs:   12360 bytes ==> GET  /i2.html
HTTP/1.1 200   0.16 secs:   12360 bytes ==> GET  /i2.html
HTTP/1.1 200   0.61 secs:   12360 bytes ==> GET  /i2.html
HTTP/1.1 200   0.61 secs:   12360 bytes ==> GET  /i2.html
HTTP/1.1 200   0.61 secs:   12360 bytes ==> GET  /i2.html
HTTP/1.1 200   0.44 secs:   12360 bytes ==> GET  /i2.html
HTTP/1.1 200   0.17 secs:   12360 bytes ==> GET  /i2.html
HTTP/1.1 200   0.21 secs:   12360 bytes ==> GET  /i2.html
HTTP/1.1 200   0.47 secs:   12360 bytes ==> GET  /i2.html
HTTP/1.1 200   0.50 secs:   12360 bytes ==> GET  /i2.html

Pour l’RL j’ai généré une page web statique remplie aléatoirement.

Une fois le test effectué (ou interrompus par ctl+c)


Transactions:                2378 hits
Availability:              100.00 %
Elapsed time:              468.79 secs
Data transferred:           28.03 MB
Response time:                0.91 secs
Transaction rate:            5.07 trans/sec
Throughput:                0.06 MB/sec
Concurrency:                4.62
Successful transactions:        2378
Failed transactions:               0
Longest transaction:            2.54
Shortest transaction:            0.16


PS : Pour quelques chose de plus fin je passe par JMetter et un site web statique de 1000 pages  générées  par un outils de ma composition.

Chaque page possède :

  • un nombre  aléatoire d’image et de paragraphes
  • chaque paragraphe est composé d’un nombre aléatoire (borne min/max paramétrable) de mots et chaque mot un nombre aléatoire de lettres (borne min/max paramétrable)

VirtualBox : Convertir une image en image VMDK splitable 2Go

Les images splitables (le système produit une machine virtuelle avec des fichiers « découpés » en portion de 2Go maxi)  sont fort pratiques puisqu’elles sont sauvegardables sur tout support (même en FAT32).

Si vous possédez une image virtualBox « monolithique » (format VDI, VHD…) sous souhaitez peut être pouvoir la convertir vers une image splitable.

Cela peut être réalisé très simplement (testé sous Linux) avec VirtualBOX


VBoxManage clonehd monrep1/img.vdi   monrep2/disk.vmdk --format VMDK --variant Split2G

PS : cela fonctionne très bien avec des images virtuelles de plusieurs centaines de giga.

PHP/CURL et TimeOut

Comment éviter les problèmes de TimeOut lors de l’exécution d’un appel CURL en PHP.

2 paramètres sont à prendre en compte :

  • Le temps pour établir la connexion avec le serveur
  • Le temps de la connexion (une fois celle-ci établie)

L’API CURL PHP répond à ce problème :

  • CURLOPT_CONNECTTIMEOUT     The number of seconds to wait while trying to connect. Use 0 to wait indefinitely.
  • CURLOPT_TIMEOUT – The maximum number of seconds to allow cURL functions to execute.
$ch = curl_init();
curl_setopt($ch, CURLOPT_CONNECTTIMEOUT ,5); 
curl_setopt($ch, CURLOPT_TIMEOUT, 400); //timeout in seconds
$response = curl_exec($ch);
curl_close($ch)

Il ne faut oublier aussi de surcharger la valeur de temps d’exécution maxima du script PHP incluant ce code, sinon ce dernier stoppera le script avant la fin du de la connexion.


set_time_limit(0);// to infinity for example

PS : n’oubliez pas d’installer CURL et son Wrapper PHP.
Exemple Debian/Ubuntu


apt-get update && apt-get dist-upgrade
apt-get install curl php5-curl

 

[Dev] Apache Maven 2 et 3 (N. De Loof & A Héritier) dispo gratuitement sur GitHub

La référence des livres Maven  « Apache Maven 2 et 3 » de  Nicolas De Loof & Arnaud  Héritier est à présent disponible gratuitement sur GitHub.

covermaven

Pour générer le livre vous devez le « builder » avec Maven ! j’aime bien l’idée 🙂

Par contre petit problème pour l’instant (en tout cas sur certaines distrib) il faut utiliser Maven 3.

[INFO] Error resolving version for 'org.asciidoctor:asciidoctor-maven-plugin': Plugin requires Maven version 3.0.0

J’ai remonté le problème auprès des auteurs.

[Node.js] Charger un fichier de configuration

Configuration d’une application

Il est obligatoire de bien séparer son code de sa config.

Dans une logique de développement rapide et léger un fichier ini est l’option la plus adaptée.

Installation du module ini

Le module ini permet de charger, sous forme d’une grappe JSON, l’ensemble d’un fichier ini.

Pour installer ini

npm install ini

Application

Exemple de fichier ini que l’on souhaiterai chargé (c’est l’exmple fournit par le doc de ini)

; this comment is being ignored
scope = global

[database]
user = dbuser
password = dbpassword
database = use_this_database

[paths.default]
datadir = /var/lib/data
array[] = first value
array[] = second value
array[] = third value

Source node.js

console.log('start');
var fs = require('fs');
var ini = require('ini');
var util = require('util');

var file = fs.readFileSync('./config.ini', 'utf-8');

//Création d'un JSON à partir du fichier ini '
var config = ini.parse(file);

// Arbre JSON -> cnsole
console.log(util.inspect(config));

// Lecteure des fuille de l'arbre JSON
console.log(config.scope);
console.log(config.database.user);
console.log(config.paths.default.datadir);

console.log('stop');

Upload multiple en HTML5/JavaScript (Partie 1)

L’upload a bien évolué depuis l’arrivée de HTML5.
Auparavant il était :

  • obligatoire d’uploader vos fichier un à un (ou alors utilisé des artifice peut rpatiques)
  • impossible d’obtenir une information sur la progression (sauf à investir dans un développement coté serveur)
  • impossible de passer (sauf à développer des hack tirés par les cheveux) par un simple Drag&Drop pour sélectionner les fichier à uploader.

A travers une série d’articles je vais maqueter un code HTML5/JavaScript permetant, au final, de réaliser de l’upload multiple d’image via Drag&Drop avec pré-visualisation et et progression de chaque image.

Au lieux de vous « coller » un code source complet et insipide j’ai décidé d’avancer par itération afin de vous montrer, un à un, tous les aspects de ce module.

Le code source est en mode « maquette » : le but est de travailler sur l’upload multiple pas sur les bonnes pratiques (validateurs, gestion des exceptions…) du codage en JavaScript.

Etape 1 : Formulaire de sélection multiple.

L’étape 1 consiste en un formulaire permettant de sélectionner plusieurs fichiers à uploader.

Tous au long de l’article j’utilisera la console JavaScript afin de loger mes actions.



Hello World


		<input id="fileToUpload" type="file" multiple="multiple" />

<script type="text/javascript">// <![CDATA[
			document.querySelector('#fileToUpload').onchange = function() {
				console.log("Stat to upload");
				var filesList = this.files;

				console.log("List Files");
				for (var i = 0, filesListLength = filesList.length; i < filesListLength; i++) {
					useFile = filesList[i];
					console.log("File num : " + i + " Name : " + useFile.name + " Size : " + useFile.size + " Type : " + useFile.type);
				}
			}

// ]]></script>


La sélection des fichier est éffectué par la balise <input> de type « file » ; l’option multiple permet la sélection multiple de fichiers.

Log du script.

Stat to upload upload.html:12
List Files upload.html:15
File num : 0 Name : AAAAA.jpg Size : 56140 Type : image/jpeg upload.html:18
File num : 1 Name : Bd7769GCYAAP7wc (3e copie).jpg Size : 56140 Type : image/jpeg upload.html:18
File num : 2 Name : Bd7769GCYAAP7wc (4e copie).jpg Size : 56140 Type : image/jpeg upload.html:18
File num : 3 Name : Bd7769GCYAAP7wc (5e copie).jpg Size : 56140 Type : image/jpeg upload.html:18

Etape 2

On souhaite ajouter une fonction de preview sur les images à uploader.
Les images ne sont pas encore uploadées, en HTML4 il n’existerait pas de solution (à moins que vous connaissiez un hack magique ?) pour réaliser cette fonction.
On effet l’image n’étant pas encore uploadée elle ne possède pas d’URL.

En HTML5 nous disposons de l’objet FileReader permettant de créer une « URL locale ».

« L’objet FileReader permet aux applications Web de  lire le contenu des fichiers (ou des tampons de données brutes) stockés sur l’ordinateur de l’utilisateur de manière asynchrone »

 https://developer.mozilla.org/fr/docs/DOM/FileReader

La fonction createThubnail crée, pour un fichier (ressource obtenu par l’input),  une preview.

On va donc lire (injecter) un file dans une instance d’un FileReader : reader.readAsDataURL(file); puis associé l’URL (attribut result de l’instance file reader) à un noeud <img> que l’on inclura dans le DOM.

La lecture étant asynchrone on ne déclenche la création du noeud via l’événement onload.

La création d’un node <img> est classique, la seule ligne intéressante est : imgElement.src = this.result; associant l’URL « local » (créé par le FileReader) à l’URL du noeud image.

			function createThumbnail(file) {
				var reader = new FileReader();

				reader.readAsDataURL(file);

				reader.onload = function() {
					var imgElement = document.createElement('img');
					imgElement.style.maxWidth = '150px';
					imgElement.style.maxHeight = '150px';

					imgElement.src = this.result;
					 document.getElementById('imgPreview').appendChild(imgElement);
				}
			}

Personnellement je ne suis pas fan de la syntaxe en this.méthode Je trouve la lisibilité mauvaise : je viens du monde de l’objet et tout cela me parait bien « sale » 😉

Etape 3 : refactoring

Je vais donc remplacer ce this.maMéthode par une injection d’événement dans la fonction, cela me semble plus « propre » (avis perso je pense que l’on pourrait troller sur le thème pendant des heures)

				reader.onload = function() {
					var imgElement = document.createElement('img');
					imgElement.style.maxWidth = '150px';
					imgElement.style.maxHeight = '150px';

					imgElement.src = this.result;
					 document.getElementById('imgPreview').appendChild(imgElement);
				}

La propriété target permet de retourner l’événement qui à déclenché l’appel de la fonction (voir http://www.w3schools.com/jsref/event_target.asp)

On obtient en itération 3 le code source suivant.



Hello World</pre>
<style><!--
			.box {
				background-color: #BBBBBB;
				border: solid 3px;
				min-height: 100px;
			}
--></style>
<pre>
 <input id="fileToUpload" type="file" multiple="multiple" /></pre>
<div class="box" id="imgPreview"></div>
<pre>
<script type="text/javascript">// <![CDATA[
			function createThumbnail(file) {
				var reader = new FileReader();

				reader.readAsDataURL(file);
				/*
				 * "L'objet FileReader permet aux applications Web de
				 * lire le contenu des fichiers
				 * (ou des tampons de données brutes)
				 * stockés sur l'ordinateur de l'utilisateur de manière asynchrone"
				 *
				 * https://developer.mozilla.org/fr/docs/DOM/FileReader
				 *
				 * Asynchrone d'ou l'obligation du reader.onload
				 * (fin du chargemen du File ) avant de lancer des traitements
				 * sur ce reader
				 *
				 */

				reader.onload = function(e) {
					var imgElement = document.createElement('img');
					imgElement.style.maxWidth = '150px';
					imgElement.style.maxHeight = '150px';
					/*
					 * Plus lisble qu'un this.result que je trouve
					 * particulièrement illisible
					 */
					imgElement.src = e.target.result;
					document.getElementById('imgPreview').appendChild(imgElement);
				}
			}

			document.querySelector('#fileToUpload').onchange = function() {
				console.log("Stat to upload");
				var filesList = this.files;

				console.log("List Files");
				for (var i = 0, filesListLength = filesList.length; i < filesListLength; i++) {
					useFile = filesList[i];
					console.log("File num : " + i + " Name : " + useFile.name + " Size : " + useFile.size + " Type : " + useFile.type);
					createThumbnail(useFile);
				}
			}

// ]]></script>


Itération 4 : upload partie client.

Nous avons un formulaire permettant de sélectionner des images et de les prévisualiser.
Ajoutons à présent la partie cliente de l’upload.

Nous utilisons l’Objet FormData bien plus avantageux que l’ancienne version Ajax.
FormaData permet :

  • d’envoyer des couple clef/data : donc plus d’un fichier à la fois si on le désire et une meilleur sémantique (gràce à la clef) coté serveur
  • gestion du TimeOut permettant de stopper un upload
  • des informations sur la progression de l’Upload
			function upload(file) {
				var xhr = new XMLHttpRequest();
				xhr.open('POST', 'srv.php');

				var formData = new FormData();
				formData.append("thefile", file);

				xhr.send(formData);

				xhr.onreadystatechange = function() {
					if (xhr.readyState == xhr.DONE) {
						console.log('Upload Done / response '+xhr.responseText);
					}
				};

			}

Pour l’instant srv.php est à l’état de mock (un simple fichier vide peut faire l’affaire), dans une logique itérative je n’ajoute qu’une fonctionnalité (ou un refactoring) par étape.

Etape 5

Codons notre upload partie serveur.
La « nouveauté » est localisé au niveau de la récupération des données, l’upload reste un upload classique tel.

Nous allons utiliser $_FILES, c’est une association des clefs (dans notre cas une seule)/propriétés fichiers.

les propriétés sont les suivantes :

  • [« name »] : nom du fichier
  • [« type »] : mime type
  • [« tmp_name »] : nom du fichier temporaire uploadé
  • [« error »] : si 0 OK
  • *[« size »] : Taille du fichier

Code de srv.php


<!--?php var_dump($_FILES["thefile"]);  /*  * array(5) {  *  ["name"]=-->  string(30) "Bd7769GCYAAP7wc (7e copie).jpg"
 * ["type"]=> string(10) "image/jpeg"
 * ["tmp_name"]=> string(14) "/tmp/phpXsSyf2"
 * ["error"]=> int(0)
 * ["size"]=> int(56140)
 */

$target_Path = "/home/lde/temp/";
echo $target_Path = $target_Path.basename($_FILES['thefile']['name'] );
echo $source_Path = $_FILES['thefile']['tmp_name'];
move_uploaded_file($source_Path , $target_Path );

?>

A venir :

  • partie 2 : gestion de la progression des upload
  • partie 3 : suppression du formulaire et remplacement par un Drag&Drop