Introduction

Cette fiche opérationnelle décrit la mise en place d'adresses IPv6 supplémentaires sous Linux / Debian / Raspbian.

IPv6 présente au moins deux avantages sur IPv4 :

  1. Plage d'adressage quasi infinie,
  2. Simplicité du routage : accès direct sans NAT.

Une IPv6 est codée sur 128 bits découpés conventionnellement en huit plages de 16 bits présentées sous en 8 groupes de 4 chiffres hexadécimaux 0–9a–F séparés par ':' tel fe80:3e0a:001a:0000:0000:0000:2215:02c3

Notes :

Une adresse locale (lien/link) non routable débute par fe80, comme celle illustrée ici. Les adresses IPv4 sont codées sur 32 bits ; leur 4 myards de possibilités comptent pour zéro par rapport aux 128 millards de milliards de milliards de myards de choix en IPv6.

Les 64 premiers bits (les 4 premiers groupes) sont presque toujours utilisés par les réseaux publics connectés sur l'internet. N'importe quel routeur d'hébergeur et n'importe quelle box internet laissent donc les 64 derniers bits disponibles pour adresser directement et publiquement une quasi infinité de machines ou d'objets sur le réseau local, d'un PC à une brosse à dent « connectée » voir à chaque poil d'icelle.

Plus besoin des contorsions nécessaires en IPv4 pour contourner la limitation du nombre d'adresses où, derrière un routeur ou une box, on trouve des adresses privées en 192.168.x.y ou en 10.a.b.c non routables sur l'internet. Oublions donc le NAT, la translation d'adresse et les conflits sur un même port avec deux serveurs derrière : en IPv6 on peut déjà au moins attribuer deux adresses publiques (externes) distinctes !

Typiquement sur une machine on aura

ip -6 addr list # or shorter: ip -6 a
1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 state UNKNOWN qlen 1000
    inet6 ::1/128 scope host 
       valid_lft forever preferred_lft forever
2: eth0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 state UP qlen 1000
    inet6 2a01:e7a:1d2:e030:2a84:7d:2b15:40eb/64 scope global mngtmpaddr noprefixroute dynamic 
       valid_lft 86082sec preferred_lft 86082sec
    inet6 fe80::a511:9db1:3f4:91f/64 scope link 
       valid_lft forever preferred_lft forever
où :

L'un des buts de cette procédure est d'associer une IPv6 publique spécifique, service par service, de sorte à les migrer d'une machine à l'autre sans toucher au reste : il suffira de supprimer l'IPv6 de la machine A et de la créer sur la machine B. Les tables de routage se mettent très vite automatiquement à jour. Bien entendu il faudra que l'application et les données soit accessibles depuis B.

Un autre but est d'asssigner une IPv6 spécifique à chaque service web, de sorte à mettre en parallèle, sur la même machine, des logiciels serveurs distinct, par exemple trois serveurs web Apache/PHP, NGINX/openResty, Zinc HTTP server de Pharo/Smalltalk, tous à l'écoute sur le même port standard https/443 mais chacun sur sa propre adresse.

En outre cela permet aussi de lancer différentes instances d'un même serveur sous les login spécifiques d'utilisateurs différents, chacun muni de ses propres droits d'accès.

Enfin, au niveau du « reverse » DNS, cela permet d'associer un nom de serveur différent à chaque adresse. Certains services logiciels imposent pratiquement cette contrainte afin de vérifier que le nom indiqué correspond bien à l'adresse, par exemple un serveur de mail mailx.tartanpion.net et un serveur DNS ns.tartanpion.net pourraient fonctionner sur la même machine, chacun sur une adresse dédiée.

Convention Maison

Nous allons voir une méthode simple de création d'adresse IPv6 rémanentes fondée sur la convention maison que le dernier segment de 16 bits suffira à distinguer jusqu'à 65 mille adresses IPv6 sur une même machine.

La convention préconise également que l'on choisisse ce segment selon le numéro de port utilisé, de sorte à suggérer le service sous jacent. Bien entendu le port reste nécessaire dans le protocole applicatif, il s'agit d'une simple redondance de confort.

Procédure

sous login privilégié (voir cette FICOP, section Opérations).

  1. On crée le script shell /etc/myIPv6 qui ajoute (et supprime) les IPv6 à partir de la liste des derniers segments à lui passer en argument

    cat > /etc/myIPv6 <<EOD...............
    #!/bin/sh
    #   jmp@scalaire 2018/12/20
    #
    # Add new IPv6 addresses on all global interfaces (eth0, enx000ab01cd02e, ...)
    # with last 16 bits segment = \$i
    # /sbin/ip -o -6 addr list
    # 2: enx000ab01cd02e    inet6 2a01:abcd:7890:e0e0:236e:3d6e:a69d:33c7/64 scope global mngtmpaddr nopr...
    # => /sbin/ip addr add 2a01:abcd:7890:e0e0:236e:3d6e:a69d:\$i dev enx000ab01cd02e
    

    if [ "\$1" != add -a "\$1" != del ] then echo "usage: \$0 add|del [last16bits]+" >&2 exit 1 fi cmd=\$1 shift for last16 in \$@ do /sbin/ip -o -6 addr list | sed -n -E \ "s,^[0-9]+:\s+([[:alnum:]]+)\s+inet6\s+([[:xdigit:]:]+):[[:xdigit:]]*/64 scope global.*,\1 \2:\$last16,p" | while read if6 ip6 do echo "\$0: run(/sbin/ip addr \$cmd \$ip6 dev \$if6)" /sbin/ip addr "\$cmd" "\$ip6" dev "\$if6" done done sleep 3 # wait for avahi ... exit 0 EOD............... chmod 0750 /etc/myIPv6
    Note : ce script agit sur toutes les interfaces où une adresse de portée globale est détectée.

  2. Test
    /etc/myIPv6 add 9999 443
    ip -6 addr list
    /etc/myIPv6 del 9999
    /etc/myIPv6 add 9999 443
    # => erreur pour 443 qui existe déjà
    /etc/myIPv6 del 9999 443
    
  3. Optionnellement, alternative de centralisation, on crée sous systemd le service en charge de tous les segments désirés
    cat > /etc/systemd/system/myIPv6.service <<EOD..........
    [Unit]
    Description=setup other public IPv6 addr
    After=network-online.target
    Wants=network-online.target
    # Before=ssh.service
    # Before=nginx.service
    

    [Service] Environment="SUFX6=9999 443 8080 22222" Type=oneshot RemainAfterExit=yes ExecStart=/bin/sh -c "/etc/myIPv6 add \$SUFX6" ExecStop=/bin/sh -c "/etc/myIPv6 del \$SUFX6"

    [Install] WantedBy=multi-user.target EOD.......... chmod 0640 /etc/systemd/system/myIPv6.service
    Notes : il est possible de spécifier des Before= si l'on veut être certain que ces IPv6 soient disponibles au lancement des services les utilisant. Ou de compter sur le WantedBy=.
    systemctl start myIPv6 # vérification :
    ip -6 addr list # si erreur ou pour info
    journalctl -exu myIPv6
    systemctl enable myIPv6 # pour redémarrage automatique
    

  4. L'alternative de délocalisation consiste à intégrer l'appel au script /etc/myIPv6 dans chacun des xxx.services avec son ou ses segments IPv6 spécifiques, par exemple dans sshd.service, on aura
    ExecStartPre=/bin/sh -c "/etc/myIPv6 add 2222"
    ExecStartPre=/usr/sbin/sshd -t
    ...
    # PostStop=/bin/sh -c "/etc/myIPv6 del 2222"
    

Alternative

Le script précédent apporte de la souplesse en cas d'allocation ou de supression dynamique au long du doux et monotone ronronnement d'un serveur qui jamais ne s'arrête, observant deci delà des va-et-vient de services et d'IP.

Si l'on souhaite des IPv6 fixées pour l'éternité, on peut naturellement les intégrer dans un plan de routage fixe, typiquement dans /etc/network/interfaces (ou /etc/netplan/XXX.yaml sous Ubuntu) ou encore, en cas d'allocation dynamique de ces adresses à la DHCP, on peut créer un script spécifique pour Network-manager / systemd. Par exemple

cat > /etc/network/if-up.d/fixed_IPv6 <<EOD...............
#!/bin/sh
# jmp@scalaire 2019/02/25 -- rajoute des IPv6 fixes sur $THIS_IFACE
# TODO : check first segments == network prefix
#        or better just concat last 64 bits (4 segmt) to net prefix
#        (see $IP6_ROUTE too)
THIS_IFACE="enp1s0" # PIN for eth1
FIXEDipv6=""
FIXEDipv6="$FIXEDipv6 2a01:e7a:1d2:e030:2a84:7d:0100:443" # client 100 web
FIXEDipv6="$FIXEDipv6 2a01:e7a:1d2:e030:2a84:7d:0200:443" # client 200 web
FIXEDipv6="$FIXEDipv6 2a01:e7a:1d2:e030:2a84:7d:0200:025" #        200 bulk mail 
FIXEDipv6="$FIXEDipv6 2a01:e7a:1d2:e030:2a84:7d:0500:443" # client 500 web
if [ "$IFACE"="$THIS_IFACE" ] && [ "$MODE"="start" ]
then
        for ip6 in $FIXEDipv6
        do
                CMD6="/sbin/ip addr add $ip6 dev $IFACE"
                echo "$0: run($CMD6)"
                $CMD6
        done
fi
exit 0
EOD...............
chmod 0750 /etc/network/if-up.d/fixed_IPv6
Ce script possède le mérite d'être automatiquement appelé à chaque remontage du lien ethernet (i.e. en cas de débranchage malheureux de la mauvaise prise ...)