Cómo prestar servicio IPv6 a través de OpenVPN

Cansado de que mi proveedor de Internet no me ofrezca IPv6 en mi red doméstica de forma nativa, decidí tomar cartas en el asunto.

Hace una década que navego con mi portátil y mi móvil a través de OpenVPN. Así no tengo que preocuparme de direcciones IP dinámicas o que la WIFI que esté utilizando sea una red hostil.

Las versiones recientes de OpenVPN soportan IPv6.

Veamos los cambios que realizo en mi configuración actual:

  • En el cliente solo hay que añadir la siguiente línea al fichero /etc/openvpn/openvpn.conf:

    tun-ipv6
    

    Este cambio indica al cliente OpenVPN que debe aceptar las configuraciones IPv6 que le envíe el servidor OpenVPN.

  • En mi servicio OpenVPN tengo que hacer dos cambios:

    • En el fichero de configuración /etc/openvpn/openvpn.conf añado lo siguiente:

      diff -r b7e120160209 -r 120fdd0d73ad openvpn.conf
      --- a/openvpn.conf      Fri Oct 20 01:18:04 2017 +0200
      +++ b/openvpn.conf      Fri Dec 01 20:28:55 2017 +0100
      @@ -1,4 +1,5 @@
       dev tun
      +tun-ipv6
       proto udp
       port 1194
      
      @@ -12,6 +13,10 @@
       group nogroup
       server 10.8.0.0 255.255.255.0
      
      +# OpenVPN solo nos deja usar un prefijo de entre 64 y 124 bits. Yo quisiera
      +# tener aquí un ::/40 para dar un ::/48 a mis usuarios.
      +ifconfig-ipv6 2001:XXXX:XXXX:XXXX:7670:6eff:7670:6e00/96 2001:XXXX:XXXX:XXXX:7670:6eff::2
      +
       keepalive 10 60
       ping-timer-rem
      

      Este cambio activa IPv6 en el servidor OpenVPN y asigna un pool ::/96 (32 bits) para los clientes. Como digo en el comentario, me gustaría tener un pool de 40 bits para poder dar 48 bits a los clientes OpenVPN. Obviamente tendría que usar direcciones IPv6 privadas, pero, en cualquier caso, mi versión actual de OpenVPN no lo permite.

      Se trata de algo a revisar en el futuro.

    • En mi caso la asignación de direcciones IP es una mezcla de direcciones estáticas y dinámicas. De momento voy a dar direcciones IPv6 de forma estática. El cambio en mi script es:

           --- a/z-script.py       Fri Oct 20 01:18:04 2017 +0200
           +++ b/z-script.py       Fri Dec 01 20:28:55 2017 +0100
           @@ -8,6 +8,8 @@
            env = os.environ
      
            IP = env["ifconfig_pool_remote_ip"]
           +IPv6 = env.get('ifconfig_pool_remote_ip6')
           +
            if "time_duration" not in env :  # Ciente conectando
                if env["common_name"] == "jcea-CSI" :
                    with open(sys.argv[1], "w") as f2 :
           @@ -100,11 +102,18 @@
                    with open(sys.argv[1], "w") as f2 :
                        # Deberiamos anadirlo tambien al fichero definido en 'ifconfig-pool-persist'
                        print >>f2, "ifconfig-push 10.8.0.244 10.8.0.1"
           +            print >>f2, "push tun-ipv6"
           +            # 10.8.0.244 -> 1008:0244
           +            print >>f2, 'ifconfig-ipv6-push 2001:XXXX:XXXX:XXXX:7670:6eff:0244:0/112 2001:XXXX:XXXX:XXXX:7670:6eff::2'
                        # Ver los comentarios mas abajo.
                        print >>f2, 'push "dhcp-option DNS 10.8.0.1"'
                        print >>f2, 'push "dhcp-option DNS 176.XX.XX.XX"'
                        print >>f2, 'push "redirect-gateway def1"'
           +            #print >>f2, 'push "redirect-gateway ipv6 def1"'
           +            #print >>f2, 'push "route-ipv6 fd00:0010:0000:0008::/64"'
           +            print >>f2, 'push "route-ipv6 2000::/3"'
                    IP="10.8.0.244"
           +        IPv6="2001:XXXX:XXXX:XXXX:7670:6eff:0244:0/112"
                elif env["common_name"] == "spamtoso" :  # No tiene IP fija
                    with open(sys.argv[1], "w") as f2 :
                        host = socket.gethostbyname("XXXXXX.rediris.es")
           @@ -124,6 +133,9 @@
                       print >>f2, 'push "dhcp-option DNS 176.XX.XX.XX"'
                       print >>f2, 'push "redirect-gateway def1"'
      
           +if IPv6:
           +    IP = IP+'-'+IPv6 if IP else IPv6
           +
            info = "%s: %s %s %s %s" %(time.ctime(), env["script_type"], \
                                       env["trusted_ip"],env["common_name"], \
                                       IP)
      

      En la línea 7 recogemos la dirección IPv6 del cliente, si la hubiese. En la línea 16 indicamos al cliente que le vamos a proporcionar una configuración IPv6. Le asigno una dirección ::/112 estática en la línea 18. Eso deja 16 bits para ese cliente. No es mi configuración favorita, pero funciona y de momento no necesito nada más. En la línea 25 inyecto una ruta IPv6 para que el cliente nos envíe el tráfico IPv6 a través del enlace OpenVPN.

  • En el servidor que ejecuta el servicio OpenVPN hay que hacer tres cambios:

    • Voy a usar NAT para IPv6. Esto es casi pecado, pero ahora mismo no me interesa nada más y usando NAT, me aseguro cierta privacidad.

      En mi configuración de cortafuegos hago esto:

      FW6=/sbin/ip6tables
      ...
      echo 1 > /proc/sys/net/ipv6/conf/all/forwarding
      $FW6 -t nat -A POSTROUTING -s 2001:XXXX:XXXX:XXXX:7670:6eff::/96 -o eth0 -j SNAT --to 2001:XXXX:XXXX:XXXX:7670:6e00:7670:6e00
      

      Estoy sacando todo mi tráfico IPv6 de OpenVPN a través de una única dirección IP IPv6.

      Sí, ya sé que eso no es lo que debería hacerse en IPv6. Tengo mis motivos :-).

    • Le añado una nueva en /etc/network/interfaces:

      iface eth0 inet6 static
              address 2001:XXXX:XXXX:XXXX:7670:6e00:7670:6e00
              netmask 64
      
    • Este servidor tiene un buen número de direcciones IP. En el NAT del tráfico OpenVPN indicamos directamente la dirección IP IPv6. El problema es el tráfico IPv6 directamente generado por el servidor, que utilizará una dirección IP origen básicamente al azar. Queremos que ese tráfico utilice una dirección IP origen específica, aunque reordenemos las direcciones en la interfaz de red. Para ello cambiamos /etc/network/interfaces de la siguiente manera:

      iface eth0 inet6 static
              address 2001:XXXX:XXXX:XXXX::1
              netmask 64
      
      # Source Address Selection
      # https://askubuntu.com/questions/304643/how-to-enforce-ipv6-source-address-selection-with-addrlabel-at-startup
      # https://www.ietf.org/rfc/rfc3484.txt
              up /sbin/ip addrlabel add prefix 2001:XXXX:XXXX:XXXX::1/128 label 1
              down /sbin/ip addrlabel del prefix 2001:XXXX:XXXX:XXXX::1/128 label 1
      # Quita prioridad al resto de direcciones
              up /sbin/ip addrlabel add prefix 2001:XXXX:XXXX:XXXX::1/64 label 99
              down /sbin/ip addrlabel del prefix 2001:XXXX:XXXX:XXXX::1/64 label 99
      

      El algoritmo de selección de la dirección IP origen en IPv6 está especificado en el rfc 3484. Aquí estamos usando la selección por label. Estamos marcando esta dirección IP IPv6 como label 1. Veamos la configuración por defecto del sistema operativo:

      root@datos:/etc/network# ip addrlabel list
      prefix ::1/128 label 0
      prefix ::/96 label 3
      prefix ::ffff:0.0.0.0/96 label 4
      prefix 2001::/32 label 6
      prefix 2001:10::/28 label 7
      prefix 3ffe::/16 label 12
      prefix 2002::/16 label 2
      prefix fec0::/10 label 11
      prefix fc00::/7 label 5
      prefix ::/0 label 1
      

      Puede verse que la ruta por defecto ::/0 tiene asignado el label 1, el mismo que hemos asignado a la dirección IP IPv6 que queremos usar como origen por defecto para el tráfico que no provenga de OpenVPN.

Veamos el resultado de todo esto:

IPv6.png

Para comprobar que la dirección IP origen del tráfico IPv6 del servidor se selecciona de forma correcta, hago un ping IPv6 a Google y veo el tráfico con un sniffer en otra ventana:

root@datos:~# ping6 www.google.es
PING www.google.es(par21s05-in-x03.1e100.net) 56 data bytes
64 bytes from par21s05-in-x03.1e100.net: icmp_seq=1 ttl=56 time=4.24 ms
64 bytes from par21s05-in-x03.1e100.net: icmp_seq=2 ttl=56 time=4.15 ms
64 bytes from par21s05-in-x03.1e100.net: icmp_seq=3 ttl=56 time=4.16 ms
^C
--- www.google.es ping statistics ---
3 packets transmitted, 3 received, 0% packet loss, time 2003ms
rtt min/avg/max/mdev = 4.155/4.186/4.243/0.084 ms

...

root@datos:~# tcpdump -n -nn host www.google.es
tcpdump: verbose output suppressed, use -v or -vv for full protocol decode
listening on eth0, link-type EN10MB (Ethernet), capture size 262144 bytes
19:33:10.571026 IP6 2001:XXXX:XXXX:XXXX::1 > 2a00:1450:4007:812::2003: ICMP6, echo request, seq 1, length 64
19:33:10.575169 IP6 2a00:1450:4007:812::2003 > 2001:XXXX:XXXX:XXXX::1: ICMP6, echo reply, seq 1, length 64
19:33:11.572005 IP6 2001:XXXX:XXXX:XXXX::1 > 2a00:1450:4007:812::2003: ICMP6, echo request, seq 2, length 64
19:33:11.576121 IP6 2a00:1450:4007:812::2003 > 2001:XXXX:XXXX:XXXX::1: ICMP6, echo reply, seq 2, length 64
19:33:12.573842 IP6 2001:XXXX:XXXX:XXXX::1 > 2a00:1450:4007:812::2003: ICMP6, echo request, seq 3, length 64
19:33:12.577959 IP6 2a00:1450:4007:812::2003 > 2001:XXXX:XXXX:XXXX::1: ICMP6, echo reply, seq 3, length 64

Me instalo la extensión IPvFox que me permite ver mi uso de direcciones IPv6 a medida que voy navegando por Internet:

IPv6_ipvfox.png

¡Misión cumplida!

Actualización 20170106: Explico por qué no uso direcciones Unique Local Address en Por qué es mala idea (a primera vista) desplegar IPv6 con 'Unique Local Address'