Artículos Guías Manuales Sistemas Linux Windows Redes MySql Binario 0 Binario Cero
Artículos Guías Manuales Sistemas Linux Windows Redes MySql Binario 0 Binario Cero

¿Por qué puedo conectarme por SSH usando IPv4 si el socket aparece como tcp6? El misterio del dual-stack en Linux

Loading

Si alguna vez has ejecutado netstat -ptnoua | grep LISTEN en un servidor Linux, seguramente te habrá llamado la atención algo como esto:

tcp        0      0 0.0.0.0:80              0.0.0.0:*               LISTEN      179/nginx: master p
tcp        0      0 0.0.0.0:443             0.0.0.0:*               LISTEN      179/nginx: master p
tcp6       0      0 :::22                   :::*                    LISTEN      1/init

Nginx aparece escuchando en tcp (IPv4 puro), mientras que SSH aparece en tcp6. Y sin embargo, te conectas sin problema por ssh usuario@192.168.x.x, una dirección claramente IPv4. ¿Cómo es posible que un socket "IPv6" acepte tráfico IPv4?

La clave: los sockets dual-stack

La respuesta está en un comportamiento del kernel de Linux conocido como direcciones IPv4-mapeadas sobre IPv6 (IPv4-mapped IPv6 addresses).

Cuando un proceso abre un socket en modo AF_INET6 y hace bind a la dirección :: (el equivalente IPv6 de "todas las interfaces"), ese socket puede comportarse de dos maneras distintas, según un parámetro del kernel:

sysctl net.ipv6.bindv6only
  • 0 (valor por defecto en Debian, Ubuntu y la mayoría de distros): el socket IPv6 es dual-stack, es decir, acepta tráfico tanto IPv6 como IPv4 en el mismo puerto.
  • 1: el socket es solo IPv6; el tráfico IPv4 necesitaría su propio socket independiente.

Con bindv6only=0, cuando llega una conexión IPv4, el kernel la traduce internamente a una dirección con el prefijo ::ffff:, por ejemplo ::ffff:192.168.1.10, y la entrega al mismo socket que está escuchando en ::. Por eso SSH, aunque aparece como tcp6, es perfectamente capaz de aceptar tu conexión IPv4: en realidad nunca ha dejado de escuchar IPv4, simplemente lo hace a través del "envoltorio" IPv6.

¿Por qué unos servicios aparecen como tcp y otros como tcp6?

Esto no depende de una regla global del sistema, sino de cómo cada aplicación decide crear sus sockets:

  • Nginx hace bind explícito a 0.0.0.0, por lo que solo escucha IPv4 (tcp). Si quisiera escuchar también en IPv6, necesitarías añadir una directiva como listen [::]:80; en su configuración.
  • OpenSSH (gestionado aquí vía socket activation de init/systemd) hace bind por defecto a ::, y al no forzar IPV6_V6ONLY, hereda el comportamiento dual-stack del kernel.

Es decir: el protocolo que ves en netstat refleja la decisión de diseño de cada aplicación, no una limitación real de conectividad.

Cómo comprobarlo tú mismo

Para confirmar el valor del parámetro del kernel:

sysctl net.ipv6.bindv6only

Y para verificar que una conexión IPv4 realmente está llegando "disfrazada" de IPv6:

ss -tnp | grep :22

Verás la IP de origen representada como ::ffff:tu.ip.v4.origen, prueba directa de que el kernel está haciendo la traducción.

Por qué esto importa en producción

Más allá de la curiosidad técnica, este comportamiento tiene implicaciones prácticas reales:

  • Firewalls e iptables: si filtras tráfico solo con reglas IPv4 (iptables) pero el servicio escucha en un socket dual-stack, una conexión IPv4 mapeada podría evaluarse bajo las reglas de ip6tables en lugar de iptables, dependiendo de cómo la trate el kernel. Es fácil abrir una brecha sin darte cuenta si solo se protege una de las dos pilas.
  • Reverse proxies (Nginx/Apache): si en algún momento configuras un listen [::]:80; sin especificar ipv6only=on|off de forma explícita, puedes terminar con un comportamiento dual-stack no intencionado, lo cual puede complicar el logging de IPs reales o el filtrado por IP de origen (por ejemplo, en configuraciones donde solo se permite tráfico desde rangos de Cloudflare, o servicios definidos).
  • Auditoría y trazabilidad: si dependes de logs para saber "quién se conectó desde dónde", conviene saber que una IP IPv4 puede aparecer registrada como ::ffff:x.x.x.x en ciertos servicios, y las herramientas de análisis deben tenerlo en cuenta.

Conclusión

Ver tcp en un servicio y tcp6 en otro no significa que uno esté mal configurado y el otro no. Simplemente refleja si la aplicación abrió el socket en modo IPv4 puro o en modo IPv6 dual-stack. Gracias a net.ipv6.bindv6only=0, un socket tcp6 escuchando en :: puede aceptar conexiones IPv4 con total normalidad, traduciéndolas mediante direcciones mapeadas. Entender este mecanismo es clave para configurar correctamente firewalls, reverse proxies y reglas de filtrado en entornos donde conviven IPv4 e IPv6.

You may also like...