2 / 2010

Astuce pf du jour

date
23 / 2 / 2010
comments
0

J'ai la chance d'avoir une bibliothèque avec un rayon informatique bien rempli dans mon Université, dernièrement j'y ai emprunté ce bouquin. Là dedans j'y ai vu une syntaxe pour décrire le réseau local que j'avais raté quand j'ai lu la doc :

ext_if = "rl0"
# Au lieu de
localnet = "{ 192.168.0.0/24, 2a01:e35:2e58:9820::/64 }"
# On peut mettre
localnet = $ext_if:network
# Ou encore
localnet = rl0:network

Pf va lui même calculer le réseau local à l'aide de l'IP du netmask et du prefixe.

Attention tout de même si vous avez plusieurs IP (v4 et v6) sur l'interface, ça peut produire des règles redondantes, en cas de doute :

pfctl -sr

EDIT (27/02/10) : En fait quand il y a des alias sur l'IP il va les évaluer mais pour ne spécifier que l'adresses (sans les alias) on peut mettre $ext_if:network:0

IPV6 step 1

date
21 / 2 / 2010
comments
1

Je viens de passer tout (ou presque) mes services en IPV6 (en gardant IPV4 of course) :

% host blog.philpep.org
blog.philpep.org is an alias for lenine.philpep.org.
lenine.philpep.org has address 82.229.137.130
lenine.philpep.org has IPv6 address 2a01:e35:2e58:9820::5

Donc l'idée c'est de faire un récit de mon passage étant donné le nombre de modifications que j'ai du faire sur la configurations des machines, des softs et des pare feu. Je vais faire tout ça en 2 ou 3 billets.

Mon FAI (free) me fournis un préfixe IPV6 2a01:e35:2e58:9820::/64, et ma topologie réseau permet sans se casser la tête de faire fonctionner tout ça. Le réseau est composé de 3 machines (+ quelques intrus en wifi), un routeur OpenBSD, un serveur FreeBSD avec 4 jails et mon laptop FreeBSD. Le mode routeur est activé sur la freebox (sans DHCP) et elle installe un 192.168.0.0/24 sur lequel sont toutes les machines (jails y compris) sont configurés, ainsi toutes les machines sont reliés physiquement sur la freebox ce qui permet d'éviter les problèmes de neighbor solicitation si seul mon routeur serait physiquement relié à la freebox (on peut passer outre avec ndp(8))

En ipv4 :

+-------------+      +----------------+       +---------+
| SRV + jails |<-----|     Routeur    |<------| Freebox |
+-------------+      +----------------+       +---------+
                      Filtrage/Routage

    En IPV4 tout le trafic passe par le routeur qui dispatche sur les jails.

Et en ipv6 :

+-------------+
| SRV + jails |<------+
+-------------+       |
                      |    +---------+
                      +----| Freebox |
                      |    +---------+
+------------+        |
|  Routeur   |<-------+
+------------+

En IPV6 le traffic est distribué par la freebox,
le routeur ne sert plus (à part les services qui
sont dessus).

La configuration ipv6 des machines sous FreeBSD :

# /etc/rc.conf
ipv6_enable="YES"
ipv6_ifconfig_rl0="2a01:e35:2E58:9820::2 prefixlen 64"
ipv6_defaultrouter="2a01:e35:2E58:9820::1"

# ... La config jail
jail_lenine_ip="192.168.0.5,2a01:e35:2E58:9820::5"
# ...

Un petit reboot (où un /etc/rc.d/pleins_de_trucs restart)

Sur OpenBSD :

# /etc/hostname.vr0
inet 192.168.0.1 255.255.255/0
inet6 2a01:e35:2E58:9820::3 64
# /etc/mygate (freebox)
192.168.0.254
2a01:e35:2E58:9820::1

Après il faut modifier votre pf.conf en supprimant inet de vos règles de filtrage comme ça on sous entend inet et inet6, concernant icmp c'est plus délicat parce qu'il y a deux protocoles.

# /etc/pf.conf
pass in inet proto icmp
pass in inet6 proto icmp6

Ensuite ça devrait tourner :

% ping6 -c 2 www.google.com
PING6(56=40+8+8 bytes) 2a01:e35:2e58:9820::2 --> 2a00:1450:8002::67
16 bytes from 2a00:1450:8002::67, icmp_seq=0 hlim=54 time=49.177 ms
16 bytes from 2a00:1450:8002::67, icmp_seq=1 hlim=54 time=53.867 ms

--- www.l.google.com ping6 statistics ---
2 packets transmitted, 2 packets received, 0.0% packet loss
round-trip min/avg/max/std-dev = 49.177/51.522/53.867/2.345 ms

% traceroute6 www.google.com
traceroute6: Warning: www.l.google.com has multiple addresses; using 2a00:1450:8002::6a
traceroute6 to www.l.google.com (2a00:1450:8002::6a) from 2a01:e35:2e58:9820::2, 64 hops max, 12 byte packets
 1  2a01:e35:2e58:9820::1  0.589 ms  0.530 ms  0.480 ms
 2  6to4-b8-e3.intf.routers.proxad.net  35.128 ms  33.359 ms  38.152 ms
....
# HO LE VILAIN TUNEL ! :>

Vous voilà en IPV6, la première chose à faire c'est reconfigurer votre parre feu, normalement vous n'aurez pas à doubler chaque lignes, enlevez juste inet là où il faut et ça passe. Les seules lignes à doubler sont les redirections (rdr), parce qu'il faut envoyer sur une adresse IPV4 ou sur une adresse IPV6.

Dans le prochain billet j'expliquerais comment passer petit à petit ses softs sur IPV6.

Dual screen avec X.org : the right way

date
14 / 2 / 2010
comments
0

Sur mon laptop, j'ai deux écrans, l'écran par defaut LVDS (en 1280x800) et un autre en VGA (en 1024x768), jusqu'ici pour avoir un dual screen je mettais un :

SubSection "Display"
    Virtual 2304 800
EndSubSection

Dans mon xorg.conf (2304 = 1280 + 1024 et 800 = max(800, 768)) et la configuration dans mon .xinitrc avec xrandr :

xrandr --output VGA --pos 1280x0 --mode 1024x768
xrandr --output LVDS --pos 0x0 --mode 1280x800
xrandr --output LVDS --left-of VGA

Ça pose quelques problèmes, par exemple quand je n'utilise pas le deuxième écran je suis obligé de commenter tout ça, il y a moyen de faire plus propre :

Section "ServerLayout"
    Identifier      "Single"
    Screen          "Screen0"
EndSection

Section "Screen"
    Identifier "Screen0"
    Device     "Card0"
    Monitor    "Monitor0"
EndSection

Section "Device"
    Identifier  "Card0"
    Driver      "intel"
    VendorName  "Intel Corporation"
    BoardName   "Mobile 945GM/GMS, 943/940GML Express Integrated Graphics Controller"
    BusID       "PCI:0:2:0"
EndSection

Section "Monitor"
    Identifier  "Monitor0"
    Option      "Position"  "0 0"
    Option      "PreferredMode" "1280x800"
EndSection

Section "ServerLayout"
    Identifier      "Dual"
    Screen          "Screen1"
EndSection

Section "Screen"
    Identifier  "Screen1"
    Device      "Card1"
    Monitor     "Monitor0"
    SubSection "Display"
        Virtual 2304    800
    EndSubSection
EndSection

Section "Device"
    Identifier  "Card1"
    Driver      "intel"
    VendorName  "Intel Corporation"
    BoardName   "Mobile 945GM/GMS, 943/940GML Express Integrated Graphics Controller"
    BusID       "PCI:0:2:0"
    Option      "monitor-LVDS"  "Monitor0"
    Option      "monitor-VGA"   "Monitor1"
EndSection

Section "Monitor"
    Identifier  "Monitor1"
    Option      "Position" "1280 0"
    Option      "RighOf" "Monitor0"
    Option      "PreferredMode" "1024x768"
EndSection

Ici je spécifie deux ServerLayout : Single et Dual, je déclare les deux moniteurs avec les résolutions qui vont bien et leur place respective (VGA en 1024x768+1280+0 et LVDS en 1280x800+0+0)

Ainsi au démarrage je choisis mon layout (par défaut il prendra le premier : Single) :

startx -- -layout Dual

Jouons avec kvm

date
13 / 2 / 2010
comments
1

J'ai codé un petit programme tout simple qui fait clignoter les leds de mon routeur dès qu'un service est par terre. Ma méthode (qui ne doit pas être la meilleure mais je m'en fout un peu) consiste à regarder régulièrement la liste des processus et détecter l'arrêt d'un service quand le processus associé n'existe plus. J'ai commencé avec un script shell, puis étant assez mauvais pour tout ce qui ressemble de près ou de loin à du script j'ai décidé de le faire en C (langage d'excellence n'est ce pas...?).

Je me suis volontairement compliqué la tache en utilisant des fonctions très proches du système, ainsi s'il est possible d'obtenir une liste de processus via ps(1) avec un popen(3) super crade ou encore sysctl(3) j'ai voulu utiliser kvm(3) kernel memory interface qui est en programmation système la méthode la plus appropriée (d'ailleurs 'ps' est codé avec ça), c'est juste une interface qui va nous permettre d'accéder au données du kernel (une copie bien entendu), ici la liste des processus en cours.

Le hic c'est que si les fonctions de kvm sont très standard l'implémentation est très différente suivant les systèmes, comme à peu près toute implémentation, l'important c'est que l'interface soit standard. Ainsi à la lecture de la structure qui décrit les processus kinfo_proc de sys/user.h on se dit chouette pleins d'info à disposition, sauf que sur OpenBSD c'est pas du tout la même forme (ni le même fichier d'ailleurs). D'où l'importance d'une interface riche et c'est ce que nous promet kvm.

Assez causé passons au code (je n'explique pas ou peu le code en dehors des fonctions kvm)

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <err.h>
#include <libgen.h>
#include <fcntl.h>
#include <limits.h>
#include <paths.h>
#include <kvm.h>
#include <sys/param.h>
#include <sys/sysctl.h>
#include <sys/wait.h>
#include <sys/user.h>

void
start_leds(void)
{
    /* Ici on execute /usr/sbin/gioctl -q gpio0 led3 (on|off)
     * suivant la parité du conteur statique 'i'
     * 60 fois avec une pause d'une seconde. */
    static int i = 1;
    switch(fork())
    {
        case -1:
            err(1, "fork");
            break;
        case 0:
            execl("/usr/sbin/gpioctl", "gpioctl", "-q", "gpio0",
                    "led3", (i%2) ? "on" : "off", (char*)NULL);
            err(1, "execl");
            break;
        default:
            wait(NULL);
            break;
    }
    sleep(1);
    if (i++ <= 60)
        return start_leds();
    return;
}

int
main(int argc, char *argv[])
{
    kvm_t *kd;
    char berr[_POSIX2_LINE_MAX];
    struct kinfo_proc *p, *procs;
    int n, j, k, found = 0;
    char **pargv;

    if (argc < 2)
        errx(1, "Usage check_proc procs ...");

    /* On accède aux données du kernel qui tourne actuellement (NULL) */
    if (!(kd = kvm_open(NULL, NULL, NULL, O_RDONLY, berr)))
        errx(1, "%s", berr);

    /* On demande la liste des processus, kinfo_proc est décrite
     * dans sys/sysctl.h sur OpenBSD et sys/user.h sur FreeBSD.
     * Il nous la donne dans un tableau continu de pointeurs de
     * taille qu'on récupère dans 'n'. */
    if (!(procs = kvm_getprocs(kd, KERN_PROC_ALL, 0, &n)))
        err(1, "kvm_getprocs: %s", kvm_geterr(kd));

    for (k = 1; k < argc; k++)
    {
        for (j = 0, p = procs; j < n; j++, p++)
        {
            /* On accède à la liste des arguments du programme qui a
             * généré le processus courant. */
            pargv = kvm_getargv(kd, p, 0);
            if (pargv && pargv[0] &&
                    strstr(pargv[0], argv[k]))
                {
                    found++;
                    break;
                }
        }
    }

    if (-1 == kvm_close(kd))
        err(1, "kvm_close");

    if (found != argc-1)
        start_leds();

    return 0;
}

Et l'exécution, on veut tester si un processus du nom de 'dhcpd' tourne sur le système, si ce n'est pas le cas on lance le clignotement de la led pendant une minute :

# gcc -o check_proc check_proc.c -lkvm
# ./check_proc dhcpd

Le mien s'exécute dans un cron toute les minutes :

* * * * * /root/bin/check_proc named dhcpd adsuck ntpd

Adsuck pour remplacer privoxy

date
11 / 2 / 2010
comments
0

Sur les trois machines clientes chez moi (dont deux avec l'OS à la pomme et de browsers douteux), filtrer les pubs du web au niveau du réseau tout entier est vital, puis il m'arrive de tester des browsers autres que firefox (dans le genre webkit-gtk/bindings vim) et là adblock ne m'est d'aucuns secours.

Pendant un temps j'utilisais privoxy sur mon routeur OpenBSD, mais je lui ai trouvé plusieurs defauts :

  • Les regexp de base sont très éfficaces mais il y a malheureusement beaucoup de faux positifs.
  • Le temps de traitement amène une lenteur parfois perceptible.
  • Je l'avais en proxy transparent, donc y'a toujours quelqu'un au bout du 'telnet machin 80' (ça peut être génant)
  • Configuration et maintenance pas franchement agréable.

Du coup en reinstallant le routeur j'ai cherché une alternative, et j'ai trouvé adsuck. C'est du filtrage dns, assez violent mais beaucoup plus rapide et tout aussi éfficace.

Pour le moment ce soft est codé par et pour OpenBSD, mais ça doit pas être méchant à compiler sur autre chose.

L'install :

# export PKG_PATH=ftp://ftp.fr.openbsd.org/pub/OpenBSD/4.6/packages/i386/
# pkg_add adsuck
# cat >> /etc/rc.local << EOF
if [ "" != "NO" -a -x /usr/local/sbin/adsuck ]; then
    echo -n ' adsuck'; /usr/local/sbin/adsuck $adsuck_flags
fi
EOF
# cat >> /etc/rc.conf.local << EOF
adsuck_flags="-c /var/adsuck -l 127.0.0.1 -p 54 -f /files/resolv.conf /files/hosts.small /files/Hosts.pub /files/local.pub"
EOF

Comme j'ai un vrai serveur DNS (pour le réseau local mais aussi pour mes domaines), j'ai lancé adsuck sur le port 54 (dns normal c'est 53) et fait une redirection pour le réseau local avec pf, adsuck va lui même servir de proxy entre le réseau local et le serveur DNS qui tourne sur localhost en particulier.

# echo "nameserver 127.0.0.1" > /var/adsuck/files/resolv.conf
# touch /var/adsuck/files/local.pub

On lance une première fois :

# adsuck -c /var/adsuck -l 127.0.0.1 -p 54 -f /files/resolv.conf /files/hosts.small /files/Hosts.pub /files/local.pub

Si ça se lance, on peut rediriger le traffic du réseau local vers adsuck :

table <local> const { 192.168.0.0/24 }
rdr pass on $ext_if proto udp from <local> to $ext_if port domain -> lo0 port 54
# Si vous êtes vraiment méchant vous pouvez enlever le 2ème $ext_if :)

Maintenant sur une machine du réseau local :

% host www.smartadserver.com dns1.proxad.net
www.smartadserver.com has address 91.103.138.65
% host www.smartadserver.com
Host www.smartadserver.com not found: 3(NXDOMAIN)

Et vous voilà avec un net plus très neutre mais épuré de toute publicité. Pour rajouter des sites, je vous ai fait créer un /var/adsuck/files/local.pub, la syntaxe est simple :

127.0.0.1 domaine_moisi.com

Et pour faire relire les listes à adsuck :

# kill -USR1 `pgrep adsuck`