Mot-clé - apache

Fil des billets

lundi 18 juin 2018

Node et socket.io derrière Apache en Reverse Proxy

Je viens de déployer une application NodeJS basée sur Angular et utilisant socket.io pour communiquer entre le "front-end" et le "back-end". Dans Firefox, je voyais toutefois ce message d'erreur :

Firefox can’t establish a connection to the server at wss://my.app/socket.io/?token=verylongtoken&EIO=3&transport=websocket&sid=verylongsid

et pourtant l'application fonctionnait parfaitement.

Après enquête, il semble que socket.io avait le bon goût de faire un "fallback" sur une méthode AJAX et s'accomodait donc du non établissement du WebSocket.

Pour régler proprement le problème, il fallait :

  • activer le module proxy_wstunnel d'Apache
a2enmod proxy_wstunnel
service apache2 restart
  • puis ajouter ces lignes dans la configuration (attention, le module rewrite d'Apache doit aussi être chargé !)
  RewriteEngine On
  RewriteCond %{REQUEST_URI}  ^/socket.io            [NC]
  RewriteCond %{QUERY_STRING} transport=websocket    [NC]
  RewriteRule /(.*)           ws://localhost:3000/$1 [P,L]

Et hop, tout était fonctionnel désormais !

lundi 28 mars 2016

LemonLDAP::NG et Let's Encrypt

Nous allons voir dans ce court article comment obtenir des certificats Let's Encrypt pour des services LemonLDAP::NG. Les chemins d'accès évoqués dans la suite correspondent à une installation de LemonLDAP::NG sur Debian Jessie à partir du paquet officiel fourni sur les dépôts de LemonLDAP::NG.

Étape 1 : obtenir un certificat pour le Manager

Nous allons commencer par autoriser l'accès au dossier .well-known dans lequel Let's Encrypt travaille (pour mémoire, il y place une clé qu'un serveur externe vient interroger, permettant ainsi d'affirmer que le demandeur gère le domaine). D'abord dans la configuration d'Apache, dans le fichier /etc/apache2/sites-available/manager-apache2.conf, nous allons remplacer la ligne

RewriteCond "%{REQUEST_FILENAME}" "!^/(?:static|doc|fr-doc|lib).*"

par

RewriteCond "%{REQUEST_FILENAME}" "!^/(?:static|\.well-known|doc|fr-doc|lib).*"

Puis, dans le manager lui-même, il faut demander à LemonLDAP::NG de ne pas bloquer l'accès au même dossier .well-known aux utilisateurs non authentifiés. Ainsi, il faut se rendre dans Virtual Hosts -> manager.mondomaine.fr -> Access rule, et ajouter une nouvelle règle :

  • Regular expression: ^/\.well-known
  • Rule: skip

Screenshot_2016-03-28_06-02-19.png

Dès lors, Let's Encrypt devrait être capable d'accéder à la clé placée dans .well-known. Nous pouvons donc démarrer le processus avec le client Let's Encrypt :

certbot certonly --renew-by-default -a webroot --webroot-path /usr/share/lemonldap-ng/manager/ -d manager.mondomaine.fr

Étape 2 : obtenir un certificat pour le portail d'authentification

Facile :

certbot certonly --renew-by-default -a webroot --webroot-path /var/lib/lemonldap-ng/portal/ -d auth.mondomaine.fr

Étape 3 : obtenir un certificat pour une application placée derrière LemonLDAP::NG

Si l'application est protégée derrière LemonLDAP::NG, il faut commencer par indiquer à LemonLDAP::NG de laisser transiter les requêtes vers le dossier .well-known. A l'image de ce qui a été fait pour le manager, il faut se rendre dans Virtual Hosts -> manager.mondomaine.fr -> Access rule, et ajouter une nouvelle règle :

  • Regular expression: ^/\.well-known
  • Rule: skip

Puis tout simplement (si l'application sous-jacente ne filtre pas les accès à .well-known) :

certbot certonly --renew-by-default -a webroot --webroot-path /var/www/mon-app -d mon-app.mondomaine.fr

vendredi 18 décembre 2015

Apache 2 : SetEnvIf et variables d'environnement, e.g. n'autoriser que certaines IP ou UserAgent à se connecter

Le serveur web Apache est extrêmement modulable quand il s'agit de filtrer des requêtes. Si tout le monde connaît les habituelles commandes Require all denied, Require all granted, Require Host example.org, nous allons voir ici comment utiliser une variable interne d'environnement et l'utiliser comme contrôle d'accès à un hôte virtuel.

Imaginons par exemple le cas suivant : nous souhaitons n'autoriser que certaines IP à se connecter, sauf pour les navigateurs dotés d'un User-Agent très spécifique.

Avant toute chose, il est nécessaire d'activer le module SetEnvIf (https://httpd.apache.org/docs/2.4/mod/mod_setenvif.html) qui permet de paramétrer une variable d'environnement d'Apache. Sous Debian (ou dérivé), cela se fera par :

a2enmod setenvif

Commençons par placer une condition sur l'IP :

SetEnvIf Remote_Addr 1.2.3.4 variable

ou alors si le serveur se trouve derrière un proxy qui fournit l'adresse d'origine dans le champ X-Forwarded-For :

SetEnvIf X-Forwarded-For 1.2.3.4 variable

La valeur fixe 1.2.3.4 peut-être remplacée par toute expression régulière de son choix.

Puis filtrons sur le User-Agent :

SetEnvIf User-Agent ^MaValeurPerso variable

Et finalement, expliquons à Apache qu'il faut interdire l'accès si aucune des conditions n'a été atteinte :

Require env variable

Dès lors, une fois la configuration rechargée, Apache n'autorisera l'accès qu'aux requêtes provenant de 1.2.3.4 ou émises par un navigateur au User-Agent correspondant à l'expression régulière ^MaValeurPerso. Ces éléments de configuration offrent beaucoup de richesse !

dimanche 11 mai 2014

Apache, protéger par mot de passe tout un hôte virtuel sauf certaines adresses : utilisation de LocationMatch

Il est assez classique d'utiliser les fonctions d'authentification d'Apache, par exemple auth_digest, pour sécuriser l'accès à un répertoire ou une application web propulsée par Apache.

Toutefois, il est parfois souhaitable de laisser en accès sans mot de passe une adresse spécifique, correspondant par exemple à une page publique ou à une fonctionnalité de l'application qu'un utilisateur lambda doit pouvoir déclencher.

Apache permet cela grâce à la direction "LocationMatch" et à des expressions régulières.

Par exemple, avec l'exemple ci-dessous placé dans un hôte virtuel correspondant à mon-app.domaine.tld :

<LocationMatch "^(?!/zone-publique)">
        AuthType Digest
        AuthName "mon-app"
        AuthDigestProvider file
        AuthUserFile /path/to/digest-password-file
        Require valid-user
</LocationMatch>

tout accès à l'hôte virtuel sera protégé par mot de passe sauf l'adresse http://mon-app.domaine.tld/zone-publique qui sera accessible à tous sans mot de passe.

Bons paramétrages !

vendredi 18 octobre 2013

Forcer la bascule vers HTTPS derrière un 'reverse proxy'

Pound (déjà évoqué à de nombreuses reprises dans ces lignes) est un 'reverse proxy' (et aussi un 'load balancer' si on souhaite utiliser ces fonctionnalités) très efficace... Il peut notamment gérer toutes les connexions en HTTPS vers vos serveurs web, les décrypter et ensuite les distribuer (bien sûr ce n'est à faire que sur un réseau local hermétique aux oreilles indiscrètes !) en HTTP à vos différents serveurs.

Selon les configurations, on acceptera les connexions en HTTP ou HTTPS ou seulement l'une ou l'autre. Parfois, on souhaite récupérer les utilisateurs qui se connectent en HTTP et les renvoyer automatiquement vers HTTPS.

Il faut alors demander au serveur web sous-jacent de réécrire les adresses, ce qui, sous Apache, se fait de la façon suivante :

        RewriteEngine on
        RewriteCond %{HTTP:X-Forwarded-Proto} !https
        RewriteRule .* https://%{HTTP_HOST}%{REQUEST_URI} [R,L]

On notera que la réécriture ne s'applique que dans les cas où le drapeau 'X-Forwarded-Proto' n'est pas déjà HTTPS.

On peut bien sûr restreindre ce paramétrage à certains dossiers seulement (même si généraliser le HTTPS aujourd'hui serait une bonne pratique).

Pour restreindre cette ré-écrire (et donc ne forcer la connexion en HTTPS) que sur certains dossiers, on pourra inclure ces instructions dans un champ Directory :

        <Directory /var/www/appli/admin>
                RewriteEngine on
                RewriteCond %{HTTP:X-Forwarded-Proto} !https
                RewriteRule .* https://%{HTTP_HOST}%{REQUEST_URI} [R,L]
        </Directory>

Ne laissez plus vos mots de passe transités en clair sur le net ! Forcez HTTPS a minima sur les zones sensibles !

mercredi 16 octobre 2013

Apache derrière un 'reverse proxy' : la magie du X-Forwarded-For

Si votre serveur Web se trouve "caché" derrière un "reverse proxy" ou toute forme de "load balancer", alors vous serez peut-être embêté en constatant que les logs d'accès d'Apache ne mentionnent par défaut que l'adresse du proxy comme adresse d'origine... Ennuyeux !

Heureusement, les esprits ingénieux qui ont conçu les "reverse proxies" ont pensé à ajouter aux paquets transmis le drapeau "X-Forwarded-For". Il suffit alors de demander à votre serveur web de prendre en compte l'IP spécifiée dans ce champ comme origine de la communication !

Dans le cas d'Apache2, la configuration intiale qui était :

LogFormat "%v:%p %h %l %u %t "%r" %>s %O "%{Referer}i" "%{User-Agent}i"" vhost_combined
LogFormat "%h%l %u %t "%r" %>s %O "%{Referer}i" "%{User-Agent}i"" combined
LogFormat "%h %l %u %t "%r" %>s %O" common
LogFormat "%{Referer}i -> %U" referer
LogFormat "%{User-agent}i" agent

devient (on remplace %h par %{X-Forwarded-For}i) :

LogFormat "%v:%p %{X-Forwarded-For}i %l %u %t "%r" %>s %O "%{Referer}i" "%{User-Agent}i"" vhost_combined
LogFormat "%{X-Forwarded-For}i %l %u %t "%r" %>s %O "%{Referer}i" "%{User-Agent}i"" combined
LogFormat "%{X-Forwarded-For}i %l %u %t "%r" %>s %O" common
LogFormat "%{Referer}i -> %U" referer
LogFormat "%{User-agent}i" agent

Pour les logs d'erreur (importants par exemple pour permettre à fail2ban, ou un autre démon du genre, d'agir), la version 2.2 d'Apache ne permet malheureusement pas de prendre en compte un format spécifique pour les logs. Il semble que cela soit corrigé dans la version 2.4 d'Apache (cf. ici). Si vous utilisez la version 2.2, alors il faudra installer le module rpaf :

aptitude install libapache2-mod-rpaf
a2enmod rpaf

puis dans /etc/apache2/conf.d/mod_rpaf :

RPAFenable On
RPAFsethostname On
RPAFproxy_ips a.b.c.d e.f.g.h
RPAFheader X-Forwarded-For

en remplaçant a.b.c.d et e.f.g.h par les adresses du reverse proxy. RPAF va veiller, dans les logs d'erreur, à remplacer l'adresse du 'reverse proxy' par l'adresse du client telle que signalée dans "X-Forwarded-For".

Et le tour est joué !

dimanche 12 juin 2011

Faire du ruby sur le web sans Rails : Sinatra

Sinatra peut être considéré comme une boîte à outil web pour Ruby très légère. Si Rails est bourré de fonctionnalités qui rendent la vie facile, il est parfois un peu démesuré d'utiliser Rails quand on veut juste exécuter un script Ruby et en afficher le résultat en ligne.

Une solution plus légère que Rails est Sinatra. [|http://www.sinatrarb.com/||http://www.sinatrarb.com/

Sinatra est un gem à ajouter à l'installation courante de Ruby :

gem install sinatra

On écrit alors le script en spécifiant les routes dans le code, par exemple :

#monApp.rb
require 'sinatra'

get '/hi' do
  "Hello World!"
end

get '/generate/:arg'
  generate(params[:arg])
end

def generate (arg)
  str=""
  5.times {
    str=str+","+arg
  }
  str
end

Ce code contient 2 routes : la première qui est appelée sur http://$host/hi et la seconde sur http://$host/generate/text.

On peut exécuter le script de manière locale en exécutant :

ruby -rubygems monApp.rb

(par défaut, le serveur de test est lancé sur http://localhost:4567)

Pour exécuter l'application via Passenger dans Apache, on ajoute dans le répertoire courant : - un dossier public (mkdir public) - un dossier tmp (mkdir tmp) - un fichier config.ru

qui contient par exemple le code suivant :

require 'rubygems'
require 'sinatra'
require '/path/to/myApp.rb'

root_dir = File.dirname(__FILE__)

set :environment, ENV['RACK_ENV'].to_sym
set :root,        root_dir
set :app_file,    File.join(root_dir, 'myApp.rb')
disable :run

run Sinatra::Application

On ajoute alors un VirtualHost convenable dans la configuration d'Apache et Passenger sert l'application Sinatra !