squid3 : journaliser les visites en https et forcer le passage par le proxy

7 réponses
Avatar
Francois Lafont
Bonjour à tous,

Je suis en train de tester squid3 pour avoir sur un LAN
un proxy authentifié. Je dispose d'une Debian Wheezy à jour
qui fait office de passerelle par défaut sur le LAN (réseau
IP 172.31.0.0/16). Le serveur fait du masquerading.

C'est donc sur cette machine que j'installe squid3 avec
un serveur MySQL pour sotcker les identifiants des comptes
autorisés à passer le proxy squid3. Pas de problème au
niveau de l'authentification, ça marche bien.

Voici mon fichier /etc/squid3/squid.conf :

#-----------------------------------------------
auth_param basic program /usr/lib/squid3/squid_db_auth --user squid --password squid --plaintext --persist
auth_param basic children 5
auth_param basic realm Veuillez vous authentifier
auth_param basic credentialsttl 5 hours
auth_param basic casesensitive off

acl auth proxy_auth REQUIRED
acl manager proto cache_object
acl localhost src 127.0.0.1/32 ::1
acl to_localhost dst 127.0.0.0/8 0.0.0.0/32 ::1
acl localnet src 172.31.0.0/16
acl SSL_ports port 443
acl Safe_ports port 80 # http
acl Safe_ports port 21 # ftp
acl Safe_ports port 443 # https
acl Safe_ports port 70 # gopher
acl Safe_ports port 210 # wais
acl Safe_ports port 1025-65535 # unregistered ports
acl Safe_ports port 280 # http-mgmt
acl Safe_ports port 488 # gss-http
acl Safe_ports port 591 # filemaker
acl Safe_ports port 777 # multiling http
acl CONNECT method CONNECT

http_access allow manager localhost
http_access deny manager
http_access deny !Safe_ports
http_access deny CONNECT !SSL_ports
http_access allow localnet auth
http_access allow localhost
http_access deny all

http_port 3128

coredump_dir /var/spool/squid3

refresh_pattern ^ftp: 1440 20% 10080
refresh_pattern ^gopher: 1440 0% 1440
refresh_pattern -i (/cgi-bin/|\?) 0 0% 0
refresh_pattern . 0 20% 4320
#-----------------------------------------------

Ensuite, je me connecte sur une machine du LAN et je configure
sur le navigateur (Iceweasel) le proxy : j'indique au navigateur
de passer par le proxy (qui est aussi la gateway) ie l'IP
172.31.0.1 sur le port 3128 pour le http et le https etc.

Ça fonctionne dans le sens où, à partir de là, si je cherche
à surfer sur le web, j'ai bien une fenêtre me demandant de
m'authentifier. Si je réponds mal je me fais jeter, sinon
ça passe.

1. Mon premier problème est que dans le fichier de log
/var/log/squid3/access.log, je ne vois aucune trace des
visites sur les sites en http*s*.

Par exemple, si je vais sur facebook (en https) et que je
me connecte même sur le site, le fichier de log ne contient
que ces 3 pauvres lignes :

1400809355.077 83 172.31.100.144 TCP_MISS/204 409 POST http://safebrowsing.clients.google.com/safebrowsing/gethash? test1 DIRECT/173.194.41.166 application/octet-stream
1400809355.174 58 172.31.100.144 TCP_MISS/200 919 POST http://ocsp.digicert.com/ test1 DIRECT/93.184.220.29 application/ocsp-response
1400809369.521 59 172.31.100.144 TCP_MISS/200 919 POST http://ocsp.digicert.com/ test1 DIRECT/93.184.220.29 application/ocsp-response

En revanche, quand c'est du http, tout est bien dans le
fichier de log. Est-il possible de loguer les urls visitées
en https ? D'après mes recherches sur le web, ça semble
impossible du fait que justement https est crypté, sauf
en utilisant une technique de « pirate » appelée le
man-in-the-middle. Est-ce correct ? Loin de moi, l'idée
de vouloir jouer les pirates. ;) En revanche je me disais
que les tous premiers échanges entre le client et le serveur
quand on cherche à contacter https://www.facebook.com/ n'étaient
pas cryptés et qu'on pouvait espérer avoir une ligne indiquant
que l'utilisateur a visité https://www.facebook.com dans le
fichier de log. Je me trompe ?

Je ne suis vraiment pas un expert dans squid que je découvre
un peu pour la première fois, mais il me semble que dans les
entreprises, ils arrivent bien à interdire facebook à leurs
employés. Cela ne se paramètre pas via le proxy ? Ils bloquent
simplement des IP de destinations sur les routeurs ?

2. Enfin, si au niveau du navigateur, je lui indique
de ne *pas* utiliser de proxy, les requêtes passent.
Je souhaiterais que ça (les requêtes http et https)
ne passe pas autrement que via le proxy (qui requiert
l'authentification). Alors, j'ai pu voir ce genre
de choses sur le web :

iptables -t nat -A PREROUTING -i eth1 -p tcp --dport 80 -j REDIRECT --to-port 3128
iptables -t nat -A PREROUTING -i eth1 -p tcp --dport 443 -j REDIRECT --to-port 3128

à faire sur le proxy/passerelle. Cela va attraper les
paquets tcp port 80 et 443 et les rediriger vers le port
3128, autrement dit vers le proxy. Et du coup, j'obtiens
ce que je veux. Mais si on cherche à consulter un serveur
en http (ou https) qui écoute sur le port 8080 (par exemple)
alors on va pouvoir passer outre le proxy. Comment peut-on
faire ? Faut-il rediriger tous les ports tcp vers le
3128 ?

Merci d'avance pour votre aide.


--
François Lafont

7 réponses

Avatar
ST
On 2014-05-23, Francois Lafont wrote:
... En revanche je me disais
que les tous premiers échanges entre le client et le serveur
quand on cherche à contacter https://www.facebook.com/ n'étaient
pas cryptés et qu'on pouvait espérer avoir une ligne indiquant
que l'utilisateur a visité https://www.facebook.com dans le
fichier de log. Je me trompe ?



Oui. C'est même pour cela qu'on ne peut pas faire de VirtualHost en
HTTPS, puisque le certificat dépend du hostname et le hostname est
crypté.

à faire sur le proxy/passerelle. Cela va attraper les
paquets tcp port 80 et 443 et les rediriger vers le port
3128, autrement dit vers le proxy. Et du coup, j'obtiens
ce que je veux. Mais si on cherche à consulter un serveur
en http (ou https) qui écoute sur le port 8080 (par exemple)
alors on va pouvoir passer outre le proxy. Comment peut-on
faire ? Faut-il rediriger tous les ports tcp vers le
3128 ?



Tous les ports TCP ne sont pas forcément du HTTP(S) ou un protocole pris
en charge par Squid.

En fait, quand on veut commencer à limiter l'accès à Internet, on
procède de façon inverse: on interdit tout et on autorise explicitement
ce qu'on veut bien laisser passer.

Un site web qui ne passerait pas sur le port 80 pour contourner un proxy
ne devrait donc pas marcher du tout.


--
François la sens-tu qui se glisse dans ton cul, la quenelle ?
Avatar
Pascal Hambourg
ST a écrit :

Oui. C'est même pour cela qu'on ne peut pas faire de VirtualHost en
HTTPS, puisque le certificat dépend du hostname et le hostname est
crypté.



Il y a eu une évolution sur ce point, même si ce n'est pas supporté partout.

Extrait de http://fr.wikipedia.org/wiki/Server_Name_Indication
"L'extension du protocole TLS, nommée SNI (Server Name Indication),
répond à ce problème en envoyant le nom DNS du domaine dans le cadre de
la négociation TLS. Cela permet au serveur de choisir le domaine virtuel
plus tôt et donc de présenter au navigateur le bon certificat contenant
le bon nom DNS. Par conséquent, avec des clients sachant utiliser SNI,
une adresse IP unique peut être utilisée pour servir un groupe de
domaines pour lequel il ne serait pas facile d'obtenir un certificat
commun."
Avatar
Pascal Hambourg
Francois Lafont a écrit :

Est-il possible de loguer les urls visitées
en https ?



Non. Le proxy ne peux connaître que le nom de domaine ou l'adresse IP du
site contenu dans la requête CONNECT du client.

D'après mes recherches sur le web, ça semble
impossible du fait que justement https est crypté, sauf
en utilisant une technique de « pirate » appelée le
man-in-the-middle. Est-ce correct ?



Correct.

2. Enfin, si au niveau du navigateur, je lui indique
de ne *pas* utiliser de proxy, les requêtes passent.
Je souhaiterais que ça (les requêtes http et https)
ne passe pas autrement que via le proxy (qui requiert
l'authentification). Alors, j'ai pu voir ce genre
de choses sur le web :

iptables -t nat -A PREROUTING -i eth1 -p tcp --dport 80 -j REDIRECT --to-port 3128



Cette règle ne fonctionne que si le proxy est aussi le routeur d'accès
vers l'extérieur, et si le proxy est configuré en mode transparent.

iptables -t nat -A PREROUTING -i eth1 -p tcp --dport 443 -j REDIRECT --to-port 3128



L'interception de HTTPS vers un proxy transparent ne marche pas pour les
raisons décrites plus haut.

à faire sur le proxy/passerelle. Cela va attraper les
paquets tcp port 80 et 443 et les rediriger vers le port
3128, autrement dit vers le proxy. Et du coup, j'obtiens
ce que je veux. Mais si on cherche à consulter un serveur
en http (ou https) qui écoute sur le port 8080 (par exemple)
alors on va pouvoir passer outre le proxy. Comment peut-on
faire ? Faut-il rediriger tous les ports tcp vers le
3128 ?



Non. Il faut rediriger les ports des protocoles que le proxy sait gérer
et bloquer les autres sauf ceux qu'on veut autoriser.
Avatar
Francois Lafont
Bonjour,

Merci à tous les deux pour vos réponses.

Le 23/05/2014 10:10, Pascal Hambourg a écrit :

Est-il possible de loguer les urls visitées
en https ?



Non. Le proxy ne peux connaître que le nom de domaine ou l'adresse IP du
site contenu dans la requête CONNECT du client.



Dans mon exemple de facebook donné dans mon premier
message, je ne disposais même pas du nom de domaine
(la simple chaîne "facebook" n’apparaissait même pas
dans les 3 lignes que j'avais obtenues dans le log).

Y a-t-il une configuration particulière à mettre
en place au niveau du squid.conf pour obtenir au
moins le nom de domaine ?

--
François Lafont
Avatar
Francois Lafont
Le 23/05/2014 13:20, Francois Lafont a écrit :

Non. Le proxy ne peux connaître que le nom de domaine ou l'adresse IP du
site contenu dans la requête CONNECT du client.



Dans mon exemple de facebook donné dans mon premier
message, je ne disposais même pas du nom de domaine
(la simple chaîne "facebook" n’apparaissait même pas
dans les 3 lignes que j'avais obtenues dans le log).



Ah, en fait c'est parce que les lignes en question
n'apparaissent qu'une fois le navigateur fermé, or
je n'allais pas jusque là dans mes tests.

--
François Lafont
Avatar
Francois Lafont
Bonsoir

Clairement il va falloir que je potasse mes régles iptables
mais je pense qu'avec les conseils que l'on m'a donnés, je
me débrouillerai.

Il me reste juste un truc que je n'arrive pas à m'expliquer.
Avec la conf que j'ai donné dans mon premier message, si
je configure mon navigateur (FF) pour qu'il utilise le proxy
et avec pour page d'accueil https://www.google.fr, alors pas
de souci : je dois m'authentifier dès l'ouverture de FF et
une fois que c'est fait, je suis sur Google. Je peux surfer
et me rendre par exemple sur https://fr-fr.facebook.com via
une requête google etc. etc. Pas d'anomalie constatée.

Maintenant, si je décide simplement de changer ma page
d'accueil sur FF en mettant https://fr-fr.facebook.com
à la place du https://www.google.fr précédent, que je
ferme mon FF et que je le ré-ouvre... alors là ça mouline
indéfiniment, à aucun moment le navigateur ne me demande
de m'authentifier et la page d'accueil facebook ne s'affiche
jamais. J'ai alors juste ça dans le log pendant que ça
mouline :

1400874252.818 0 172.31.100.144 TCP_DENIED/407 3734 CONNECT fr-fr.facebook.com:443 - NONE/- text/html
1400874254.755 0 172.31.100.144 TCP_DENIED/407 4330 POST http://safebrowsing.clients.google.com/safebrowsing/gethash? - NONE/- text/html

Si je stoppe FF dans son chargement vain de la paga d'accueil
(sans fermer l'application) et que je rentre à la main
https://www.google.fr/ dans la barre d'adresse, alors là
j'ai bien une demande d'authentification et ça semble bien
fonctionner une fois que je suis authentifié. Mais par contre,
toujours sur cette même session où je suis désormais
authentifié, si je surfe (via une recherche google etc.) pour
aboutir au site de facebook, alors là ça mouline à nouveau.

Bref, le simple fait de changer la page d'accueil de
mon FF afin de passer de https://www.google.fr/ à
https://fr-fr.facebook.com semble lui poser problème.

Est-ce que vous avez une idée de pourquoi ?

--
François Lafont
Avatar
Francois Lafont
Bonsoir,

Le 23/05/2014 21:56, Francois Lafont a écrit :

Clairement il va falloir que je potasse mes régles iptables
mais je pense qu'avec les conseils que l'on m'a donnés, je
me débrouillerai.



Voilà le genre de script iptables que j'ai fini par obtenir.
Ce script (copié ci-dessous) est indiqué en post-up au niveau
de la dernière interface configurée dans le fichier
/etc/network/interfaces de l'hôte qui est un routeur/pare-feu
NAT avec une interface LAN et une interface WAN (indiquées dans
le script). L'hôte fait serveur DNS (via un forwarding), serveur
DHCP au niveau du LAN, serveur de temps au niveau du LAN et il
fait serveur proxy pour le LAN via Squid3.

Ce script est censé forcer les hôtes qui se trouvent dans
le LAN à passer par le proxy Squid3 pour faire du Web.
Je trouve que ce n'est pas si facile que ça de faire en
sorte que les règles iptables fassent exactement ce que l'on
veut qu'elles fassent et je serais très intéressé si vous
avez des remarques sur le script ci-dessous. Peut-être ne
fait-il pas exactement ce que je veux et peut-être même
que j'ai laissé sans m'en rendre compte des trous béants.
Si vous avez des remarques, suggestions etc. je suis
preneur.

Merci d'avance.

-------------------------------------------------------
#!/bin/sh

IF_WAN='eth0'
IP_WAN='192.168.0.0/24'

IF_LAN='eth1'
IP_LAN='172.31.0.0/16'
IP_IFLAN='172.31.0.1/32' # IFLAN has static IP

SQUID_PORT='3128'

echo 1 >/proc/sys/net/ipv4/ip_forward

# Reset the configuration.
iptables -t filter -F
iptables -t filter -X
iptables -t filter -P INPUT ACCEPT
iptables -t filter -P FORWARD ACCEPT
iptables -t filter -P OUTPUT ACCEPT

iptables -t nat -F
iptables -t nat -X
iptables -t nat -P PREROUTING ACCEPT
iptables -t nat -P OUTPUT ACCEPT
iptables -t nat -P POSTROUTING ACCEPT

iptables -t mangle -F
iptables -t mangle -X
iptables -t mangle -P PREROUTING ACCEPT
iptables -t mangle -P INPUT ACCEPT
iptables -t mangle -P OUTPUT ACCEPT
iptables -t mangle -P FORWARD ACCEPT
iptables -t mangle -P POSTROUTING ACCEPT

# Default policy is "DROP".
iptables -t filter -P INPUT DROP
iptables -t filter -P FORWARD DROP

# Allow loopback traffic.
iptables -t filter -A INPUT -i lo -j ACCEPT

# Allow icmp from everywhere.
iptables -t filter -A INPUT -p icmp --icmp-type any -j ACCEPT

# Allow ssh connections from everywhere.
iptables -t filter -A INPUT -p tcp --dport 22 -j ACCEPT

# Allow DHCP requests from LAN.
iptables -t filter -A INPUT -i $IF_LAN -p udp --dport 67 -j ACCEPT

# Allow NTP trafic from LAN.
iptables -t filter -A INPUT -i $IF_LAN -p udp -s $IP_LAN --dport 123 -j ACCEPT
iptables -t filter -A INPUT -i $IF_WAN -p udp -d $IP_IFLAN --dport 123 -j ACCEPT

# Allow DNS requests from LAN
# and allow DNS responses from WAN.
iptables -t filter -A INPUT -i $IF_LAN -s $IP_LAN -p udp --dport 53 -j ACCEPT
iptables -t filter -A INPUT -i $IF_WAN -s $IP_WAN -p udp --sport 53 -j ACCEPT

# Allow squid requests from LAN
# and allow host to receive http[s] responses from WAN.
iptables -t filter -A INPUT -i $IF_LAN -p tcp -s $IP_LAN --dport $SQUID_PORT -j ACCEPT
iptables -t filter -A INPUT -i $IF_WAN -d $IP_WAN -p tcp -m multiport --sports 80,443 -m state --state ESTABLISHED,RELATED -j ACCEPT

# Redirect ports 80 and 443 to squid.
iptables -t nat -A PREROUTING -p tcp -m multiport --dports 80,443 -j REDIRECT --to-port $SQUID_PORT -s $IP_LAN

# Set masquerading.
iptables -t nat -A POSTROUTING -s $IP_LAN -j MASQUERADE

# Logging dropped packets.
iptables -t filter -A INPUT -j LOG --log-level 5 --log-prefix "filter:INPUT dropped "
iptables -t filter -A FORWARD -j LOG --log-level 5 --log-prefix "filter:FORWARD dropped "
-------------------------------------------------------


--
François Lafont