OpenVPN on OpenBSD

So this is a small article, because I wanted to see if I could write more if I just wrote small things like that about a single, simple thing I did, without too much detail and fluff

Also, I’m writing this in English, while I usually write in French. I’m switching language because I believe English is a lot easier to express technical concepts in, or at least I’m more fluent in it when it comes to expressing technical concepts, and I believe now that my audience (at least, the people I know/talk to on twitter/IRC/etc…) speak or read English much more than French, and so it makes more sense for me to write in English here. Therefore, I’ll be writing in English only on this blog from now on.

(French version :) De plus, j’écris ceci en Anglais, alors que j’écrivais ici habituellement en Français. Je change de langue, parce qu’il me semble qu’il est plus facile d’exprimer des concepts techniques en Anglais qu’en Français, ou en tout cas que cela m’est plus facile personnellement, mais aussi parce que je pense que mon audience (ou en tout cas, les gens que je connais/auxquels je parle sur twitter/IRC/etc…), parlent ou lisent l’Anglais bien plus que le Français, et il est donc plus logique pour moi d’écrire en Anglais ici. J’écrirais donc uniquement en Anglais sur ce blog a partir de maintenant.

So, now that that’s done, I can go on and write that “small article” I promised at the top.

So, the idea is that I had a FreeBSD OpenVPN box that I used to have a semi-decent Internet connection while at school (my school blocks all ports that are not tcp/80 or tcp/443 or udp/53, basically. And apparently udp/443 too…). I wanted to try running that VM on OpenBSD, because of three things :

  1. I really like OpenBSD, and wanted to have a VM that I could do some experiments on without breaking all of my stuff,
  2. I found a way to run OpenBSD on the provider I used for that box, vultr, and
  3. why not?

Anyway, so once you’ve installed the OS, the first thing to do is

$ doas pkg_add openvpn

well okay the first thing to do is to

# vi /etc/doas.conf

and put this in it :

permit keepenv :wheel as root
permit nopass root as root

once this is done, you can now go and install the packages, before creating the CA:

$ doas pkg_add vim openvpn easy-rsa
$ cd /usr/local/share/easy-rsa
$ doas ./easyrsa init-pki
$ doas ./easyrsa gen-dh
$ doas ./easyrsa build-ca [nopass]
$ doas ./easyrsa build-server-full [CN of the server] [nopass]
$ doas ./easyrsa build-client-full [CN of a client] [nopass]

please note that you can use passwords on all of those, but then you’ll have to type them every time you use one of them. I see no problem with having a password on the CA and the client, but the server should be able to restart by itself in my opinion.

Anyway, now we can write the config for OpenVPN:

$ doas mkdir /etc/openvpn/
$ doas vim /etc/openvpn/openvpn.conf

We’ll run with these settings :

dev tap
tls-server
cert /usr/local/share/easy-rsa/pki/issued/[CN of the server].crt
key /usr/local/share/easy-rsa/pki/private/[CN of the server].key
ca /usr/local/share/easy-rsa/pki/ca.crt
dh /usr/local/share/easy-rsa/pki/dh.pem
proto udp
port 53
verb 3
status /var/log/openvpn-status.log
ifconfig 172.16.0.10 255.255.0.0
route-gateway 172.16.0.10
persist-key
persist-tun
keepalive 10 120
server 172.16.0.0 255.255.0.0
client-to-client
tls-cipher TLS-DHE-RSA-WITH-AES-256-CBC-SHA TLS-DHE-RSA-WITH-CAMELLIA-256-CBC-SHA
push "route 172.16.0.0 255.255.0.0"

Of course, feel free to edit that to match whatever you need.

Anyway, the next thing we need to do is to configure pf.

What, you thought that was it? Of course we’re gonna filter this, it’s an internet-facing server!

$ doas vim /etc/pf.conf

So, here is the pf configuration file :

#       $OpenBSD: pf.conf,v 1.54 2014/08/23 05:49:42 deraadt Exp $
#
# See pf.conf(5) and /etc/examples/pf.conf

set block-policy drop
set skip on lo0
block return in on ! lo0 proto tcp to port 6000:6010
match in all scrub (no-df random-id max-mss 1440)

block log all

match out on egress from (tap0:network) to any nat-to (egress:0)
pass out quick

# ssh
pass in on egress proto tcp from any to (egress) port 22

# mosh
pass in on egress proto udp from any to any port 60000:61000

# snmp
pass in on egress proto udp from [IP of my SNMP server] to any port 161
pass in on egress proto udp from [IPv6 block of my SNMP server]/48 to any port 161

# openvpn
pass in on egress proto udp from any to (egress) port 53
pass in on egress proto udp from any to (egress) port 443 rdr-to (egress:0) port 53
pass in on tap0

So, this should be easy enough to read, but just in case : we skip lo, we block X, we scrub weird packets, we block and log by default.

Then, we NAT everything that comes out of the VPN and to the ‘net. We let what comes from the server out too, tho that could be improved…

The next three blocks are easy, and then in the OpenVPN block, we let in port udp/53, we redirect port udp/443 to udp/53, and we let everything in from the VPN.

We have to reload pf and add a sysctl knob if we want to actually route packets coming from the VPN:

$ doas pfctl -f /etc/pf.conf
$ echo 'net.inet.ip.forwarding=1' | doas tee -a /etc/sysctl.conf

And now, we simply enable the OpenVPN service, and we’re done:

$ doas rcctl enable openvpn
$ doas rcctl enable pflogd
$ doas rcctl start openvpn
$ doas rcctl start pflogd
$ doas rcctl ls on    # to check

That’s it! It was actually pretty easy, I guess.

Also, if you don’t know what’s wrong and want to get a detailed log, run/usr/local/sbin/opvnpn --verb 11 --config /etc/openvpn/openvpn.conf

Seeya!


PoC||GTFO; now on a NEW MIRROR near you thanks to A NETWORKING STUDENT and world-famous NGINX REDIRECTION

Date Wed 12 October 2016
By Wxcafe
Category Note

Le titre dit a peu près tout : j’ai récemment commencé a lire les PoC||GTFO (Proof of Concept OR Get The Fuck Out), dont j’avais entendu parler depuis quelques années mais pour lesquels je n’avais jamais réussi jusqu’ici à prendre du temps pour les lire. C’est désormais chose faite (je les ai imprimés, c’est plus confortable, vous pouvez en voir pas mal d’extraits surtwitter).

Pour celleux qui ne seraient pas au courant, je vais résumer vite fait : PoC||GTFO est un journal publié depuis 2013 par un groupuscule composé de pas mal de gens faisant de la sécurité informatique (Manul Laphroaig, the Grugq, Ange Albertini, notamment…) et composé d’articles soumis par de nombreuses personnes sur des sujets techniques en général centrés sur la sécurité et/ou l’exploitation. Un ton très spécifique, et de nombreux easter-eggs cachés dans chaque numéro, associés a une qualité exceptionnelle des articles, à très vite fait connaître ce journal. Une des clauses de la licence autorisant la lecture étant de le partager, je fais donc un mirroir.

Le mirroir est situé ici : https://wxcafe.net/pub/PoC||GTFO. A noter qu’il faut bien distinguer https://wxcafe.net/pub de https://pub.wxcafe.net, et que d’autres publications intéressantes pourraient un jour se trouver dans ce dossier (je ferai a ce moment la un post pour en avertir, ou pas.)

Informationellement,


Redondance de routeurs, avec OpenBSD et FreeBSD

Depuis le début de mon DUT (il y a deux ans), j’ai découvert le monde du réseau, alors que j’étais plus système auparavant. Au cours de ce processus, j’ai pu observer quelques coutumes étranges de ce milieu. Ainsi donc, dans cet étrange domaine, il arrive parfois qu’on cherche à avoir un réseau stable pendant une période relativement longue. Bien évidemment, ceci se trouve être un problème Complexe®, a cause notamment des différents constructeurs de matériel réseau, et des différents systèmes d’exploitation des machines qui font passer les chatons dans les tuyaux.

Bref, en général on règle ce problème de façon relativement simple : en utilisant un système stable, par exemple OpenBSD. Cependant, ça ne suffit pas toujours: on peut aussi rencontrer des erreurs hardware. Et puis même OpenBSD peut rencontrer des problèmes softwares aussi, de temps en temps. Il paraît. J’ai lu un truc la dessus quelque part.

Bref, après cette intro complètement objective, on va parler de redondance de routeurs (c’est a dire la mise en place de deux routeurs hardwares en même temps, avec un qui prend le relai de l’autre en cas de problème). On va aussi faire en sorte qu’ils utilisent deux réseaux externes différents (d’opérateurs séparés, par exemple), pour faire bonne mesure. Vu que c’est un projet pour mon DUT à la base, et qu’on a fait que du Linux la bas, j’ai décidé de le réaliser avec un OpenBSD et un FreeBSD, sur un laptop et une Cubieboard 2 (une board ARM qui traînait chez moi), en utilisant des VLANs (puisqu’ils n’ont qu’une seule NIC). C’est aussi pour ça qu’il y a un FreeBSD, vu que la Cubieboard ne supporte qu’assez mal OpenBSD (en tout cas d’après mon expérience)

Tout d’abord, je vais mettre ici le rapport produit pour mon DUT, comme le veut la tradition du TL;DR (tu le sens mon LaTeX?). Si vous voulez pas lire cette explication, vous pouvez lire l’autre explication qui est en PDF et orientée pour des profs de DUT. Si vous êtes prof de DUT j’imagine que ça peut être intéressant.

Bon, donc la première chose a faire c’est de définir quelques trucs. La redondance, on l’a dit, c’est le fait d’avoir plusieurs équipements effectuant une tâche similaire, pour qu’en cas de panne l’un prenne la place de l’autre sans interruption. Quelques acronymes :

  • CARP, Common Address Redundancy Protocol, est un protocole (développé par OpenBSD pour remplacer VRRP) qui permet de faire de la redondance entre des équipements IP, en leur permettant de partager une adresse IP en switchant rapidement en cas de problème avec l’un des équipements.

  • PF, Packet Filter, est le firewall d’OpenBSD et de FreeBSD. Enfin, des versions différentes. Mais l’idée est la. (en pratique, la version de FreeBSD est plus ancienne mais supporte le multi-CPU, contrairement a celle d’OpenBSD (mais bon, on connait le support multi-CPU d’OpenBSD…)).

  • PfSync, Packet Filter Synchronisation, est un service qui permet de synchroniser la table d’état de deux instances de PF. De cette façon, quand un des deux crashe, le second peut reprendre les connexions en cours et évite de couper trop de transmissions.

  • IfStated est un petit programme qui permet de vérifier l’état d’une interface réseau régulièrement et de lancer des commandes en fonction de l’état de celle ci.

Bon, maintenant que ces définitions sont claires, passons à la réalisation. Le système OpenBSD sera le serveur primaire, et le FreeBSD sera la réplique, car OpenBSD est capable de routage multipath (répartition du traffic entre deux routes de manière égale), ce que FreeBSD ne sait pas faire. Ainsi, si R1 (la machine OpenBSD) est primaire, elle est capable de transférer une partie du traffic vers R2 (la machine FreeBSD). Si elle s’arrête de fonctionner, R2 n’a pas besoin de faire de multipath, puisqu’a ce moment la une seule route valide est encore disponible.

La première chose à faire est de configurer le réseau sur nos deux machines. Puisqu’elles ont toutes les deux une seule interface réseau, nous utilisons des VLANs (en conjonction avec un switch correct, je vous laisse trouver la configuration de celui-ci. Il faut connecter les deux machines sur des ports Trunk). Le VLAN 300 sera utilisé pour le réseau interne, le 400 pour le réseau externe A et le 500 pour le réseau externe B. Ainsi, on aura un réseau qui ressemble à ceci :

╭──╮   ╭─────────────╮   ╭──╮
│  │   │    Switch   │   │  │
│R1│   │             │   │R2│
│  │   │             │   │  │
╰──╯   ╰─────────────╯   ╰──╯
  ╚════════╝     ╚═════════╝

en terme physique, et ceci :

            ╔══════╗   ╔══════╗
╭──╮    ╭────╮    ╭─────╮    ╭────╮   ╭──╮
│OP│    │    │    │     │    │    │   │OP│
│  │    │ R1 │    │ LAN │    │ R2 │   │  │
│A │    │    │    │     │    │    │   │B │
╰──╯    ╰────╯    ╰─────╯    ╰────╯   ╰──╯
  ╚════════╝                   ╚════════╝

au niveau réseau. On va aussi utiliser le réseau 30.30.30.0/24 sur le réseau interne pour cet exemple.

Pour ce faire, on configure les routeurs ainsi :

R1 (OpenBSD):

/etc/hostname.em0:

up

/etc/hostname.vlan0:

inet 30.30.30.1 255.255.255.0 30.30.30.255 vlan 300 vlandev em0

/etc/hostname.vlan1:

dhcp vlan 400 vlandev em0

R2 (FreeBSD):

/etc/rc.conf

[...]
vlans_dcw0="300 500"
ifconfig_dwc0_300="inet 30.30.30.2 netmask 255.255.255.0"
ifconfig_dwc0_500="DHCP"

Une fois ceci fait, nos machines sont configurées sur leurs réseaux externes respectifs (via DHCP, adaptez si votre réseau externe utilise une autre méthode) et sur le réseau interne. Il faut bien entendu remplacer les noms d’interfaces (em0, dcw0) par le noms des interfaces présentes sur vos machines.

Nous allons maintenant configurer la redondance elle même avec CARP. Le réseau avec lequel nous allons nous retrouver ressemble à ceci :

                 ╭───────╮
           ╔═════│  VIP  │══════╗
           ║     ╰───────╯      ║
           ║         ║          ║
╭──╮    ╭────╮    ╭─────╮    ╭────╮   ╭──╮
│OP│    │    │    │     │    │    │   │OP│
│  │    │ R1 │    │ LAN │    │ R2 │   │  │
│A │    │    │    │     │    │    │   │B │
╰──╯    ╰────╯    ╰─────╯    ╰────╯   ╰──╯
  ╚════════╝                   ╚════════╝

La configuration de CARP se fait en fait comme pour une interface réseau classique :

R1:

/etc/hostname.carp0:

vhid 125 pass pwd12345 carpdev vlan0 advbase 3 advskew 1 state master
30.30.30.254 netmask 255.255.255.0

R2:

/etc/rc.conf:

[...]
ifconfig_dwc0_300_alias0="vhid 125 advbase 3 advskew 200 \
  state backup pass pwd12345 alias 30.30.30.254/24"

Une fois que CARP est mis en place, nous configurons PF, pour filtrer les flux que nous laissons passer sur notre réseau. Les configurations suivantes, différentes pour R1 et R2 (puisque FreeBSD et OpenBSD n’utilisent pas les mêmes versions de PF), sont évidemment à modifier en fonction de votre installation: elles sont très minimales (ne laissant même pas passer le http…)

Pour R1:

/etc/pf.conf:

set skip on lo

# définition des variables
int="30.30.30.0/24"
ext="0.0.0.0/0"
int_addr="30.30.30.1"
int_if="vlan0"
ext_if="vlan1"

# defaut : bloquage
block all

# vérification des paquets, anti-spoofing
antispoof for $int_if
antispoof for $ext_if

# nous laissons passer l'icmp
pass proto icmp

# nous mettons en place le NAT de l'interieur vers Internet
pass in on $int_if from $int to any keep state
pass out on $ext_if from $int to $ext nat-to $int_if keep state

# carp, pfsync et dhcpsync
pass out on $int_if proto carp keep state
pass quick on $int_if proto pfsync keep state
pass in on $int_if proto udp to any port 8067 keep state
pass out on $int_if proto udp to any port 8067 keep state

# nous laissons passer les connexions SSH vers le routeur
pass in on $int_if proto tcp from $int to $int_addr port ssh keep state
pass out on $int_if proto tcp from $int_addr port ssh to $int keep state

Et pour R2:

/etc/pf.conf:

set skip on lo

# définition des variables
int="30.30.30.0/24"
ext="0.0.0.0/0"
int_addr="30.30.30.2"
int_if="dwc0.300"
ext_if="dwc0.500"

# défaut : bloquage
block all

# vérification des paquets, anti-spoofing
antispoof for $int_if
antispoof for $ext_if

# nous laissons passer l'icmp
pass proto icmp

# nous mettons en place le NAT de l'interieur vers Internet
nat on $ext_if from $int to any -> ($ext_if)
pass in on $int_if from $int to any keep state
pass out on $ext_if from any to $ext

# carp, pfsync et dhcpsync
pass out on $int_if inet proto carp keep state
pass quick on $int_if inet proto pfsync keep state
pass in on $int_if inet proto udp to port 8067 keep state
pass out on $int_if inet proto udp to port 8067 keep state

# nous laissons passer les connexions SSH vers le routeur
pass in on $int_if inet proto tcp from $int to $int_addr \
port ssh keep state
pass out on $int_if inet proto tcp from $int to $int_addr \
port ssh keep state

Une fois que PF est configuré, on passe a pfsync, qui permet de synchroniser l’état de deux instances de PF, même de versions différentes (je trouve ce truc génial):

Pour R1:

/etc/hostname.pfsync0:

syncdev vlan0 syncpeer 30.30.30.2

Et pour R2:

/etc/rc.conf:

pfsync_enable="YES"
pfsync_syncdev="dwc0.300"
pfsync_syncpeer="30.30.30.1"

Passons à ifstated. Puisque R1 supporte le multihoming mais pas R2, nous allons faire en sorte que R1 aie une route multipath vers R2. De cette façon, R1 (qui est la machine principale pour CARP, et reçoit donc toutes les connexions venant du réseau interne), transmet la moitié de ces connexions vers R2, qui les gère comme nécessaire. Si R1 arrête de fonctionner, R2 récupère l’ensemble des connexions (grâce a CARP), qui ne sont pas interrompues (grâce a pfsync). Si R2 arrête de fonctionner, ifstated rentre en action et retire la route multipath de R1 vers R2, ce qui permet d’éviter de transmettre la moitié des connexions à un routeur qui ne fonctionne plus (c’est en général une chose a éviter).

Par conséquent, la configuration d’ifstated n’a à être effectuée que sur R1 :

/etc/ifstated.conf:

peer = '( "ping -q -c 1 -w 3 30.30.30.2>/dev/null" every 5 )'

state auto {
    if $peer
        set-state multihome
    if ! $peer
        set-state singlehome
}
state multihome {
    init {
        run "route add -mpath default 30.30.30.2"
    }
    if ! $peer
        set-state singlehome
}
state singlehome {
    init {
        run "route delete default 30.30.30.2"
    }
    if $peer
        set-state multihome
    }

init-state auto

Enfin, dernier point a configurer, la synchronisation DHCP. Elle nous permet de faire en sorte que les machines gardent les mêmes adresses IP même si un des deux routeurs reste en rade pendant une période prolongée. On configure donc isc-dhcpd sur les deux routeurs, comme suit:

R1:

/etc/dhcpd.conf:

authoritative;
ddns-update-style none;

failover peer "dhcp-failover" {
    primary;
    address 30.30.30.1;
    port 8067;
    peer address 30.30.30.2;
    peer port 8067;
}

subnet 30.30.30.0 netmask 255.255.255.0 {
    option routers 30.30.30.254;
    option domain-name-servers 30.30.30.254;
    pool {
        failover peer "dhcp-failover";
        max-lease-time 86400;
        range 30.30.30.10 30.30.30.250;
    }
}

Et pour R2:

/usr/local/etc/dhcpd.conf:

authoritative;
ddns-update-style none;

failover peer "dhcp-failover" {
    secondary;
    address 30.30.30.2;
    port 8067;
    peer address 30.30.30.1;
    peer port 8067;
}

subnet 30.30.30.0 netmask 255.255.255.0 {
    option routers 30.30.30.254;
    option domain-name-servers 30.30.30.254;
    pool {
        failover peer "dhcp-failover";
        max-lease-time 86400;
        range 30.30.30.10 30.30.30.250;
    }
}

Et voilà! Notre réseau ressemble désormais à ça (j’ai repris le schéma de mon rapport, j’ai pas le courage de le refaire en texte encore):

schéma

avec le PC1 qui représente le réseau local.

Si vous avez bien lu la configuration du serveur DHCP, il reste encore à mettre en place un serveur DNS écoutant sur l’IP virtuelle, donc a priori synchronisé entre les deux routeurs. Comme c’est quelque chose de simple a mettre en place et que c’est assez bien documenté ailleurs, je laisse cette tâche comme exercice aux lecteurs-ices.


Envie partout, temps nulle part

Ça fait assez longtemps que j’ai pas posté ici, j’en suis bien conscient, et j’écris donc ce petit post pour dire que c’est le cas, pourquoi c’est le cas, et que ça va pas durer.

J’ai beaucoup de choses qui me prennent pas mal de temps en ce moment, notamment:

  • un boulot, je suis en stage
  • la rédaction d’un rapport, puisque… je suis en stage
  • la recherche d’une alternance, pour l’an prochain
  • un bon nombre de démarches administratives variées
  • insert autre raison here.

par contre, j’ai /énormément/ de choses dont j’aimerais parler, notamment:

  • de la redondance de routeurs, avec CARP, PfSync, dhcpsync et ifstated
  • du backup de confs réseau avec Oxidized
  • des mésaventures avec debian et nvidia
  • du junk hacking sur une liseuse ukrainienne
  • des backups automatisés via puppet
  • encore d’autres trucs, que j’ai oublié la comme ça mais ça va revenir

Du coup, vous inquietez pas, j’ai pas oublié ce blog, et je reviens vite.


Let's Encrypt, enfin

Update 2016-09:

Ça fait un certain temps maintenant (depuis mai 2016, en fait) que le script letsencrypt a été renommé certbot. Le dépot est donc maintenant https://github.com/certbot/certbot, mais letsencrypt-auto est toujours un lien symbolique vers le bon script, il n’est donc pas nécessaire de changer les commandes.


Vous avez peut être vu que ce blog, entre autres sites que j’administre, n’est disponible depuis quelques jours qu’en HTTPS, et avec un certificat valide. Bon, si vous êtes là, vous avez déjà entendu parler de Let’s Encrypt, mais pour les deux trois du fond on va résumer:

LE est une nouvelle autorité de certification (ceux qui valident les certificats SSL), basée sur une organisation, et dont le but est de fournir des certificats valides, automatiquement et gratuitement. Leur certificat racine est signé par IdenTrust, et est donc considéré valide par tous les navigateurs modernes.

Bon, maintenant qu’on est tous au même point, voyons comment ça marche. Depuis dix jours LE est ouvert en bêta publique, donc il n’est plus nécessaire de préciser les domaines pour lesquels on veut un certificat sur un formulaire, comme c’était le cas pendant la période de bêta fermée. Le système qui est utilisé repose sur le protocole ACME (Automatic Certificate Management Environment), qui automatise complètement la signature des certificats. Du coup, les certificats que délivre LE ne sont valides que 90 jours, ce qui serait super chiant avec une autorité de certification classique, mais qui la veut simplement dire qu’il faut mettre un cron en place.

Bref, comment mettre en place vos certificats? On va faire ça sans trop modifier vos sites, et en automatisant au maximum. LE utilise, dans son système par défaut, un fichier sur le site web, dont le serveur de certification vérifie l’existence lors de la requête (si le fichier est présent avec le bon contenu, c’est que le client tourne bien sur ce domaine, et donc que la personne qui a demandé le certificat contrôle bien le domaine). Ce fichier est situé dans un dossier dans la racine, .well-known. Plutôt que de se faire chier a gérer ce dossier pour tous nos vhosts nginx, on va simplement créer un alias vers un dossier commun sur le système de fichier, que tous les vhosts partagerons, et qui permettra aussi de valider tous les domaines pour lesquels on veut un certificat à la fois (avec un AltName) (sur un seul serveur, par contre. Enfin si vous voulez vraiment vous pouvez faire des mounts cross-serveurs (avec du sshfs ou des trucs du genre), mais c’est un peu sale quand même. Et faudra quand même distribuer le certificat après, donc bon…).

Donc, on va rajouter ça dans nos blocs server :

location /.well-known {
    alias /srv/letsencrypt/.well-known;
}

(bien sûr il faut créer le dossier, hein.)
Après, on git clone https://github.com/letsencrypt/letsencrypt, dans /opt/ ou dans /usr/local/, peu importe, on le clone quelque part, et on cd dans le dossier en question. Une fois là, on demande un certificat :

sudo ./letsencrypt-auto certonly \
    -a webroot \
    --webroot-path /srv/letsencrypt/ \
    -d <domaine> \
    -d <altName1> \
    -d <altName2> \
    --server https://acme-v01.api.letsencrypt.org/directory

Normalement, maintenant, on a un certificat valide dans /etc/letsencrypt/live/<domaine>/. Reste à configurer nginx pour qu’il serve nos sites en https en utilisant notre nouveau certificat. Perso, j’utilise une template qui ressemble à ça :

server {
    listen 80;
    listen [::]:80;
    server_name SERVERNAME;
    return 302 https://$server_name$request_uri;
}

server {
    listen 443 ssl;
    listen [::]:443 ssl;
    ssl_certificate /etc/letsencrypt/live/DOMAIN/fullchain.pem;
    ssl_certificate_key /etc/letsencrypt/live/DOMAIN/privkey.pem;
    ssl_dhparam /etc/nginx/dhparams.4096;
    ssl_protocols TLSv1 TLSv1.1 TLSv1.2;
    ssl_ciphers "EECDH+ECDSA+AESGCM EECDH+aRSA+AESGCM EECDH+ECDSA+SHA384 EECDH+ECDSA+SHA256 EECDH+aRSA+SHA384 EECDH+aRSA+SHA256 EECDH+aRSA+RC4 EECDH EDH+aRSA !RC4 !aNULL !eNULL !LOW !3DES !MD5 !EXP !PSK !SRP !DSS";
    ssl_prefer_server_ciphers on;
    add_header Strict-Transport-Security "max-age=15552000; includeSubDomains; preload";

    root SERVERROOT;

    index index.html index.htm;

    server_name SERVERNAME;

    server_tokens off;
    client_max_body_size 5m;

    access_log /var/log/nginx/access.log;
    error_log /var/log/nginx/error.log;

    location /.well-known {
        alias /srv/letsencrypt/.well-known;
    }

    location / {
        try_files $uri $uri/ =404;
    }
}

Alors c’est pas /tout à fait ça/ d’un point de vue parano du TLS (genre je devrais désactiver TLS 1.0 et EECDH+aRSA+RC4, notamment) mais ça marche pas trop mal et c’est plus compatible comme ça (mon telephone est sous Android 4.4, donc je suis content d’avoir encore TLS 1.0 par exemple).

Vous pouvez ajouter votre domaine à la liste préloadée dans Chrome/ium, Firefox, IE, Edge, Safari, le Tor Browser Bundle, etc…ici (oui ça fait clairement site de phishing, mais apparemment c’est serieux…)

Enfin, il nous faut un renouvellement automatique, puisque notre certificat ne sera valide que 90 jours. On va utiliser un cron tout con, avec un script :

00 01 */14 * * /usr/local/bin/cert-renew 2>&1 | mail -s "certificates renewal report" <votre email>

(oubliez pas que ça doit aller dans le crontab du root) Et le script qui va bien :

#!/bin/bash

if [[ $UID != 0 ]]; then
    echo "please run as root"
    exit 1
fi

cd /opt/letsencrypt/

git pull 2>&1 >> /dev/null


# Renewing the cert
./letsencrypt-auto certonly \
    -a webroot --webroot-path /srv/letsencrypt \
    -d <domaine> \
    -d <altName1> \
    -d <altName2> \
    --server https://acme-v01.api.letsencrypt.org/directory \
    --renew \
    2>&1

systemctl restart nginx
exit 0

Notez bien le --renew qui spécifie qu’on renouvelle le certificat, le git pull qui met à jour le client, et le systemctl restart nginx qui prend en compte le nouveau certificat automatiquement

Et puis voilà, normalement avec ça vous devriez pouvoir chopper des certificats valides. C’est plutôt cool, en pratique.

Merci Let’s Encrypt


OpenWRT, l'USBNet, et l'histoire des 4Mo

Donc, j’ai récemment obtenu un TP-Link TL-MR12U, qui est vendu comme “routeur 3G portable”, mais qui est en réalité une grosse batterie avec une antenne wifi, un port USB, et un port Ethernet. Perso, ça me va, vu que je comptais de toute façon pas prendre un deuxième abonnement 3G juste pour ce truc là (surtout vu la couverture 3G qu’on se tape en France…)

Bref, tout ça pour dire : quand j’ai reçu ce truc, j’ai tout de suite commencé par y installer OpenWRT (parce que de 1, je parle pas chinois, et de 2, j’aime bien avoir des firmwares corrects sur mes routeurs). Bon, c’est super simple, il suffit de chopper ce fichier [binaire] la, et de trouver la page d’update (pas forcément super simple en chinois, mais avec un peu de temps, ça se fait. C’est celle avec un bouton upload). Ensuite on upload l’image sur le bouzin, et c’est parti. Pas de signatures, pas de vérifications, osef total, mais bon pour le coup ça m’arrange.

Une fois ceci fait, je me trouva bien démuni de ne pas pouvoir utiliser le partage de connexion USB de mon intelliphone android, car l’image OpenWRT par defaut ne comprend pas USBNet, et ne peut donc pas créer de réseau sur de l’USB. Qu’à cela ne tienne, me dis-je! Je vais l’installer! Je courra donc installer le package grâce à opkg. Las! Le système n’avait plus de place.

… Atta. Le système avait plus de place? J’ai encore rien mis dessus!

Eh bah ouais. Il se trouve que TP-Link, en 2015, trouve que 4Mo de flash sur un routeur, c’est largement suffisant, et que de toute façon personne aura jamais besoin de plus.

Serieux, mettre 8Mo c’était tellement plus cher? u_u

Bon, bref, je vais pas m’étendre la dessus. J’ai décidé de saisir mes petits bras, et de tenter de pousser bien fort pour convaincre OpenWRT qu’il était tout a fait possible de faire rentrer à la fois le système de base avec LuCi, uhttpd, un serveur DHCP, etc; et USBNet, dans 4Mo. Ça à pas été vraiment facile, et j’ai du virer pas mal de trucs, mais… ça fonctionne!

Bon, alors, comme je suis quelqu’un de sympa, je vais vous filer à la fois le fichier de config et l’image finale. Si vous voulez pas utiliser une image qui vient d’un mec que vous connaissez pas, vous pouvez toujours la rebuilder vous même. Mais avant ça, je vais vite fait expliquer ce qui est dans l’image et ce qui n’y est pas

Alors, pour faire rentrer tout ça, vous vous doutez que j’ai du faire quelques concessions. J’ai donc viré tout ce qui a trait à PPP, PPPoE, le client DHCPv6, tous les outils de debug, quelques fonctionnalités de busybox, et bien sûr opkg. Dans ce qui à été ajouté, simplement ce qui est nécessaire au fonctionnement de l’USBNet.

Une petite modification doit être effectuée pour que le tout fonctionne : le fichier package/feeds/luci/luci/Makefile doit être modifié pour que la dépendance sur luci-proto-ppp ne soit plus présente. Ainsi, on passe de

LUCI_DEPENDS:= \
    +uhttpd +uhttpd-mod-ubus +luci-mod-admin-full +luci-theme-bootstrap \
    +luci-app-firewall +luci-proto-ppp +libiwinfo-lua +IPV6:luci-proto-ipv6

à

LUCI_DEPENDS:= \
    +uhttpd +uhttpd-mod-ubus +luci-mod-admin-full +luci-theme-bootstrap \
    +luci-app-firewall +libiwinfo-lua +IPV6:luci-proto-ipv6

Une fois que c’est fait, ça devrait mieux marcher (et ça sauve un peu d’espace…)

Bon. Le fichier de config est , l’image finale est , et j’ai une petite surprise.

Bien sûr, le switch situé sur le côté du TL-MR12U ne fonctionne pas sous OpenWRT de base, parce que c’est un truc lié au hardware et que du coup c’est assez compliqué à gérer sur une base de matos aussi grande que celle d’OpenWRT. Bah j’ai à peu près trouvé comment le faire fonctionner. Voilà le code :

#!/bin/sh
if [ $ACTION == "released" ]; then
    if [ $BUTTON == "BTN_0" ]; then
        # Position is 3G
        logger "slider 3G"
    elif [ $BUTTON == "BTN_1" ]; then
        # Position is Router
        logger "slider Router"
    fi
elif [ $BUTTON == "BTN_1" ] || [ $BUTTON == "BTN_0" ]; then
    if grep -qe "sw1.*in  hi" /sys/kernel/debug/gpio\
    && grep -qe "sw2.*in  hi" /sys/kernel/debug/gpio; then
        # Position is AP
        logger "slider AP"
    fi
fi

Et ça va dans /etc/hotplug.d/button/00-buttons (créez le chemin, il existera pas à la base). Du coup là comme ça ça fait rien, ça loggue juste les events. Mais comme vous êtes pas cons vous avez peut être deviné qu’on pouvait très bien activer l’USBNet que quand l’interrupteur est en position 3G, le wifi et l’ethernet quand il est en position AP, et juste la batterie quand il est en position Router. Par exemple.

Tiens, d’ailleurs. Pour activer le partage de connexion, suffit pas d’ajouter le support USBNet. Il faut aussi configurer le système pour qu’il demande un lease DHCP, toussa. Du coup vous pouvez (peut être, j’ai pas testé) le faire par LuCi, mais sinon vous pouvez le faire en CLI :

uci del network.wan
uci set network.wan=interface
uci set network.wan.ifname=usb0
uci set network.wan.proto=dhcp
uci commit network
ifup wan

Et pouf, ça marche.

Voilà. Amusez vous bien avec votre grosse batterie portable, qui fait maintenant point d’accès wifi/partage de connexion 3G/whatever.


les NUCs et le HDMI-CEC

J’ai récemment récupéré une télé. Ce post ne se centrant pas sur cette télé, passons rapidement sur ce qui y est lié : ne souhaitant pas “profiter” du paysage audiovisuel français (ou PAF), et ayant nombre de films et séries acquis tout a fait légalement (hmm hmm) stockés sur mon serveur local, je souhaitais brancher sur ma télévision un système me permettant de regarder ces films et séries, et possiblement quelques sources de vidéos en ligne (Youtube, Netflix, etc…) simplement.

Ayant un Raspberry Pi 1 qui trainait, j’ai décidé d’installer OpenELEC dessus et de voir ce que ça donnait. Le résultat n’étant pas satisfaisant (a cause des difficultés du RPi a faire fonctionner tout ça), j’ai décidé d’upgrader le système.

J’ai donc acquis un NUC D34010WYK (attention, les nouveaux modèles ne fonctionnent pas pour ce qui suit), un adaptateur HDMI-CEC pour celui-ci, et un SSD mSATA, en me disant que je pourrais sans trop de problème faire tourner Kodi sur un debian, avec en plus Steam pour faire du streaming depuis mon desktop. L’autre avantage de tourner sur du Intel, c’est de pouvoir mater Netflix (puisque le plugin kodi approprié utilise chrome, et ne fonctionne (a ma connaissance) que sur x86).

J’ai donc reçu après un certain temps le matériel sus cité, que j’ai avidement monté, avant de me rendre compte que le manuel de l’adaptateur Pulse-Eight était [PDF]assez médiocre. J’ai donc cherché plusieurs heures, avant de trouver [DE]ce post expliquant comment brancher l’adaptateur. Je vais donc résumer ici le processus, ce qui devrait rendre la tache a la fois plus simple pour les autres personnes cherchant l’information, et pour moi si je dois remonter ce système.

Pour faire simple, le NUC présente trois headers séparés : un dual-USB, un dit “Front Panel”, et un appelé “Custom Solution Header”. Les trois sont utilisés ici. La première chose a faire est de brancher les fiches grises et rouges sur le Custom Solution Header: le branchement doit être fait ainsi :

Custom Solution
  ┌─┬─┬─┬─┬─┐
  │g│ │·│r│·│
  ├─┼─┼─┼─┼─┤
  │·│·│·│·│·│
  └─┴─┴─┴─┴─┘

  g ➔ fiche grise
  r ➔ fiche rouge
  · ➔ pin inutilisé
    ➔ espace vide (sans pin)

Une fois cela fait, il faut brancher le Front Panel. Heureusement, c’est plus facile, puisqu’il n’y a qu’une seule fiche a brancher ici : la orange.

  Front Panel
  ┌─┬─┬─┬─┬─┐
  │·│·│·│·│·│
  ├─┼─┼─┼─┼─┤
  │ │·│o│·│·│
  └─┴─┴─┴─┴─┘

  o ➔ fiche orange
  · ➔ pin inutilisé
    ➔ espace vide (sans pin)

Enfin, il faut encore brancher les fiches restantes sur le header dual-USB. Étant donné que ce header contient deux fois les pins nécessaires a un branchement USB, il est possible de brancher les cables de plusieurs façons.

   Dual-USB
  ┌─┬─┬─┬─┬─┐
  │b│B│v│n│·│
  ├─┼─┼─┼─┼─┤
  │·│·│·│·│ │
  └─┴─┴─┴─┴─┘

  b ➔ fiche bleue
  B ➔ fiche Blanche
  v ➔ fiche verte
  n ➔ fiche noire
  · ➔ pin inutilisé
    ➔ espace vide (sans pin)

Tous les branchements étant effectués, il faut maintenant remonter la bête (attention a ne pas déranger les branchements avec les antennes Wifi, par exemple), la brancher, et vérifier que tout démarre bien. Il faut aussi changer un paramètre dans le BIOS intel : dans Power➔Secondary Power Settings, il faut que “Deep S4/S5” soit désactivé. Ceci permettant a la connection HDMI-CEC de démarrer et le NUC.

Ne reste plus ensuite qu’a installer un système digne de ce nom dessus!


SSL - STARTTLS

Le chiffrement SSL pour les services en ligne est un problème relativement récent, par rapport a l’histoire d’Internet. Sa mise en place pose problème : les protocoles existants ne s’accommodent qu’assez mal de recevoir soudainement un flot de données chiffrées, mais développer de nouveaux protocoles est complexe et n’apporte rien d’intéressant. Pour palier a ce problème, deux solutions sont apparues.

Le première consiste à faire écouter les services sur un autre port, dans un tunnel SSL. De cette façon, le service existant écoute normalement, mais il ne répond pas directement aux requêtes. A la place, un tunnel SSL est mis en place, et les requêtes et les réponses passent dans le tunnel (ou elles apparaissent donc chiffrées pour l’extérieur). Cela permet de proposer un service chiffré en modifiant de façon minimale le programme, au prix de devoir aussi changer tous les clients, et de devoir les orienter sur un autre port.

L’autre approche qui a été utilisée est une approche d’upgrade. La communication commence en mode non chiffré, puis le client demande l’upgrade de la connexion vers le mode chiffré s’il le supporte, les deux machines machines font un handshake SSL et la communication continue a travers le tunnel SSL. Le service peut continuer a écouter sur son port habituel, et seuls les clients capables de passer en SSL le feront, ce qui permet de faire la “mise a jour” en douceur.

Il est souvent demandé quelle est la meilleure méthode pour mettre en place un service – laisser un port pour le SSL et un pour le trafic non chiffré, ou bien un seul, avec STARTTLS, qui upgrade les connexions si nécessaire.
La réponse est que STARTTLS est plus interessant, pour plusieurs raisons. Tout d’abord, il permet de n’utiliser qu’un seul port : ça permet de simplifier la configuration du firewall. En plus de ça, il permet aux clients “anciens” (ceux qui ne supportent pas SSL, donc ceux qui devraient être changés) de toujours se connecter, même si cela signifie que leurs informations seront transmises en clair. Surtout, il permet d’éviter aux utilisateurs d’avoir a configurer leurs clients. Si le client supporte le chiffrement, il l’activera de lui même s’il voit qu’il est disponible.
Bref, mettez en place du STARTTLS, et pas du SSL. C’est mieux pour la sécurité de tout le monde.


Manettes : Hori vs. PDP

Si vous avez comme moi une Wii U et Smash 4, vous vous êtes probablement rendus
compte de quelques petits trucs : tout d’abord, Smash est bien plus drôle
a plusieurs. Ensuite, la Wii U peut être contrôlée avec énormément de “choses”
sans trop réflechir, il y a le Wii U Gamepad, les Wiimotes, les Pro Controllers pour Wii et Wii U, et d’autres. Vous aurez aussi remarqué que le Gamepad n’est pas du tout un moyen de jouer a Smash acceptable, ni les wiimotes. Les pro controllers fonctionnent, mais ne valent pas les bonnes vieilles manettes Gamecube.

Cela étant, si comme moi vous avez, euh, “ouvert” le mode vWii de votre Wii U, vous avez surement un disque dur/une clé USB connecté au dos de votre Wii U, et donc pas assez de ports libres pour connecter l’adaptateur GC pour Wii U a votre console.

Heureusement pour vous, Nintendo a pensé a une solution (et comme d’habitude avec Nintendo, c’est une solution a moitié satisfaisante…) : les classic controller, mais en forme de manettes Gamecube.

Nintendo a donc filé ses licences et ses designs a deux boites, qui se sont empressées de faire des manettes et de ramasser des brouettes d’argent, en faisant des manettes Gamecube qui se connectent a des Wiimotes.

Nous allons ici voir deux modèles, un de chacune des boites en question
PDP et Hori.

Toutes les photos présentes dans cet article sont disponibles en plus grande taille en cliquant dessus

Commençons par le modèle de chez Hori :

Hori_face

Comme vous pouvez le voir, la manette ressemble beaucoup a une véritable manette de Gamecube : a part le bouton Turbo et le bouton Home, le reste est parfaitement identique a une véritable manette Gamecube. A noter que les boutons centraux (Home, Start, Select, et Turbo) sont en caoutchouc mou et pas en plastique dur.

Hori_dos

Nous voyons déjà le premier gros problème de cette manette : les gâchettes ne sont en réalité que des boutons : c’est logique puisque c’est comme ça que les classic controller sont faits, mais c’est décevant tout de même

Hori_CM

On peut voir ici que la qualité de l’assemblage n’est pas extraordinaire, et on remarque une soudure mal faite a l’emplacement du stick gauche.

Hori_Cstick

Le stick c n’est pas fixé au reste de la manette. J’ai essayé de démonter plus avant les différentes parties de la manette, mais les câbles n’avaient pas l’air de très bonne qualité, et j’ai préféré abandonner l’idée plutôt que de casser la manette.

Hori_coque

On peut voir que la coque est complètement vide, et que les gâchettes sont bien en réalité de simple boutons. Il y aurait presque la place de mettre la carte mère d’une Wiimote entière la dedans…


Passons maintenant a la manette PDP.

PDP_face

Au premier coup d’œil, on remarque que la manette PDP ressemble beaucoup moins a une manette gamecube. Cependant, la prise en main est exactement la même. On regrettera tout de même les sticks, pas aussi agréables que ceux de la Gamecube, et les boutons transparents (mais c’est un problème de goût).

PDP_dos

Les gâchettes sont des vraies gâchettes! C’est impossible normalement, mais PDP a été très intelligent sur le coup, comme on va le voir juste après.

PDP_CM

Comme vous pouvez le voir, la qualité générale est bien meilleure, avec bien moins de colle, et pas de soudage raté. Toutes les cartes filles sont bien attachées a la carte mère, et la structure en plastique est renforcée. Mais surtout, on peut voir deux cartes filles qui sortent de façon étrange de la carte mère, de façon péremptoire perpendiculaire…

PDP_CF

Vous l’avez deviné, ces deux “cartes filles” servent en réalité de connecteurs aux boutons situés sur les gâchettes, qui sont de “vraies” gâchettes en cela qu’elles sont faites de la même manière que les vraies (avec un ressort, etc) mais qui sont en réalité des boutons (évidemment, puisque cette manette est en fait un classic controller), par opposition aux véritables gâchettes analogiques.

Quelques photos des gâchettes en question :

PDP_G_1

PDP_G_2

PDP_G_3

PDP_G_4


Vous l’aurez compris, je préfère la version PDP de ces “Fight Pad”, la finition semble plus solide, les gâchettes sont parfaites, les boutons centraux ne sont pas en caoutchouc cheap, et bien que les sticks soient moins confortables, le reste est parfait. Si vous préférez avoir une manette dont la prise en mains esttotalement identique a celle des manettes Gamecube, cependant, la version Hori vous conviendra probablement mieux, a part les gâchettes, malheureusement.


Docker et les ebooks sur Twitter

Date Sat 28 February 2015
By Wxcafe
Category Note

Vous avez peut être déjà entendu parler de Docker. Si ce n’est pas le cas, voila les bases : Docker est un système de containers. Les containers sont une forme particulière de virtualisation, ou le kernel n’est pas virtualisé, mais ou les processus du système hôte sont séparés de ceux des systèmes invités. Cela est possible depuis longtemps sous FreeBSD avec les Jails, mais n’est devenu possible sous linux que récemment grâce aux cgroups, qui permettent justement de séparer des groupes de processus. Le principe de Docker est donc d’avoir une machine hôte sur laquelle s’exécutent plusieurs conteneurs Dockers, chacun séparé des autres et de l’hôte, mais utilisant tous le même kernel. Cela pose quelques questions en terme de sécurités, puisque la séparation est bien plus fine qu’avec de la virtualisation classique. En effet, ici, en trouvant un exploit kernel, un attaquant aurait potentiellement la capacité de remonter jusqu’à l’hôte, puisqu’il n’est pas vraiment séparé des invités.

Quoi qu’il en soit, Docker permet donc de virtualiser a moindre coût des systèmes GNU/Linux. “Mais pourquoi utiliser Docker, dans ce cas”, vous demandez-vous peut être, “puisque Xen peut faire la même chose, et plus (notamment, Xen est capable de virtualiser autre chose que GNU/Linux)?”. Et bien c’est très simple : Docker apporte la simplicité de déploiement d’applications. Les conteneurs Dockers peuvent être décrit en un fichier, nommé Dockerfile, qui permet de répliquer un conteneur en quelques minutes sur un autre hôte, en une commande. Le Docker Hub permet aussi de récupérer rapidement et facilement un grand nombre d’images déjà configurées.

Maintenant que nous avons expliqué rapidement ce qu’était Docker, voyons le rapport avec les ebooks et Twitter.

Les comptes dits “ebooks” (le nom vient a l’origine de horse_ebooks, voir ici pourquoi) sont des bots twitter utilisant des Chaines de Markov, avec les tweets d’un utilisateur “source” comme corpus, pour produire des tweets ressemblant a ceux de l’utilisateur source. Nous allons voir maintenant comment en installer un.

C’est, comme disent certaines personnes, “fun”.

Il existe de nombreuses librairies écrites pour créer ce genre de bots, cependant dans ce cas nous nous concentrerons sur celle-ci, qui est une lib ruby créée par @m1sp, qui gère pour nous a la fois l’API twitter et la génération des messages.

Cependant, cela n’explique toujours pas le lien avec Docker. Ce lien est très simple : nous utilisons un container pour faire tourner les bots. Depuis la version 3, la gem twitter_ebooks permet de faire tourner plusieurs bots dans une seule instance. Cependant, il est toujours plus sûr d’isoler les bots, et les containers dockers permettent de les déployer sur n’importe quelle machine (celleux qui ont déjà tenté de mettre en place une application basée sur ruby sauront le problème que cela pose habituellement). Pour ce faire, j’ai créé un repo github qui contient toutes les pièces nécessaires pour mettre cela en place : le bot en lui même, les deux Dockerfiles, etc.

Le fonctionnement du bot est simple : après avoir installé la gem twitter_ebooks, vous archivez le corpus de l’utilisateur source avec ebooks archive <username> <filename> (c’est du json) , puis vous convertissez le json en fichier utilisable par le bot : ebooks consume <filename>. Cela fait, démarrer le bot revient a lancer le container : docker run -d <container name> Pour plus d’informations, allez voir la documentation Docker

Bien entendu, dans l’idéal il faudrait mettre a jour les corpus de chaque utilisateur régulièrement. Cela est très simple a mettre en place avec un simple script cron :

00 00 * * *    /usr/local/bin/ebooks archive username /usr/local/ebooks/main/corpus/username.json >> /var/log/ebooks/update.log 2>&1
00 05 * * *    cd /usr/local/ebooks/main/ && /usr/local/bin/ebooks consume corpus/username.json >> /var/log/ebooks/update.log 2>&1
00 10 * * *    docker rm -f bots >/dev/null 2>&1
00 15 * * *    docker rmi bots  > /dev/null 2>&1
00 20 * * *    cd /usr/local/ebooks/main/ && docker build --rm -t bots . >> /var/log/ebooks/build.log 2>&1
00 25 * * *    docker run -d --name bots bots >> /var/log/ebooks/run.log 2>&1

Les 5 minutes entre chaque commande sont laissées pour empécher que deux commandes ne s’executent en même temps.

Et voila, vous avez un container Docker qui fait tourner une application en ruby toute sale, et votre système hôte reste propre. Bien sûr, ce n’est qu’un exemple des possibilités de Docker : par exemple, on peut aussi faire tourner des applications “usuelles” dedans, puisque l’overhead de Docker est minimal, et beaucoup d’autres applications existent.