iptables: ¿destino para enrutar paquetes a una interfaz específica?

25

Mi servidor doméstico tiene dos interfaces principales eth1(una conexión a Internet estándar) y tun0(un túnel OpenVPN). Me gustaría usar iptablespara forzar la salida de todos los paquetes generados por un proceso local propiedad del UID 1002 tun0y todos los demás paquetes para salir eth1.

Puedo marcar fácilmente paquetes coincidentes:

iptables -A OUTPUT -m owner --uid-owner 1002 -j MARK --set-mark 11

Ahora, me gustaría poner alguna regla en la cadena POSTROUTING (probablemente de la tabla de cambios) para que coincida con los paquetes marcados con 11 y enviarlos tun0, seguido de una regla que coincida con todos los paquetes y los envíe eth1.

Encontré el objetivo ROUTE, pero parece que solo reescribe la interfaz de origen (a menos que lo esté leyendo incorrectamente).

¿Es iptables capaz de esto? ¿Tengo que perder el tiempo con la tabla de enrutamiento (a través de ip routeo solo los routecomandos heredados )?

Editar: pensé que tal vez debería proporcionar más información. No tengo otras reglas de iptables en este momento (aunque puedo crear algunas reglas para llevar a cabo tareas no relacionadas en el futuro). Además, la salida de ip routees:

default via 192.168.1.254 dev eth1  metric 203
10.32.0.49 dev tun0  proto kernel  scope link  src 10.32.0.50
85.17.27.71 via 192.168.1.254 dev eth1
192.168.1.0/24 dev eth1  proto kernel  scope link  src 192.168.1.73  metric 203

No he tocado la tabla de enrutamiento: así es como está actualmente (aunque parece bastante sucia). Lo siento, pero no tengo el routecomando heredado instalado en esta máquina.

Y la salida de ip addr(por supuesto, eth0 y eth2 se puede ignorar, son NIC que no se están utilizando actualmente):

1: lo: <LOOPBACK,UP,LOWER_UP> mtu 16436 qdisc noqueue state UNKNOWN
    link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
    inet 127.0.0.1/8 scope host lo
    inet6 ::1/128 scope host
       valid_lft forever preferred_lft forever
2: eth0: <BROADCAST,MULTICAST> mtu 1500 qdisc noop state DOWN qlen 1000
    link/ether 1c:6f:65:2a:73:3f brd ff:ff:ff:ff:ff:ff
3: eth1: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc pfifo_fast     state UP qlen 1000
    link/ether 00:1b:21:9d:4e:bb brd ff:ff:ff:ff:ff:ff
    inet 192.168.1.73/24 brd 192.168.1.255 scope global eth1
    inet6 fe80::21b:21ff:fe9d:4ebb/64 scope link
       valid_lft forever preferred_lft forever
4: eth2: <BROADCAST,MULTICAST> mtu 1500 qdisc noop state DOWN qlen 1000
    link/ether 00:1b:21:6a:c0:4b brd ff:ff:ff:ff:ff:ff
5: tun0: <POINTOPOINT,MULTICAST,NOARP,UP,LOWER_UP> mtu 1500 qdisc     pfifo_fast state UNKNOWN qlen 100
    link/none
    inet 10.32.0.50 peer 10.32.0.49/32 scope global tun0

Editar: Tengo algo que funciona, pero no reenvía paquetes marcados a tun0. Básicamente, agregué una tabla (11) y usé:

ip route add table 11 via 10.32.0.49 dev tun0
ip rule add priority 10000 fwmark 11 table 11

Cuando acabo de sudo -u user1000 wget -qO- whatismyip.orgobtener la dirección IP externa de mi casa, pero si lo hago sudo -u user1002 wget -qO- whatismyip.org, también obtengo la dirección IP de mi casa (pero debería obtener la IP en el otro extremo del túnel OpenVPN).

La ejecución iptables -vLconfirma que los paquetes coinciden con la regla de marcado, pero no parecen estar siguiendo la regla de enrutamiento.

EDITAR: He pasado mucho tiempo en esto, y aunque todavía no funciona, creo que estoy un poco más cerca.

La regla de iptables tiene que estar en la manglecadena de SALIDA de la tabla. Creo que también necesito una regla de MASQUERADE en la natcadena POSTROUTING de la tabla, para establecer la dirección de origen. Sin embargo, el cambio de ruta que se produce después del cambio de OUTPUT no funciona correctamente.

He pasado 5 horas en esto ahora, así que me estoy tomando un descanso y probablemente volveré a hacerlo más tarde esta noche o mañana.

Ethan
fuente

Respuestas:

26

Recientemente he tenido un problema similar, aunque un poco diferente. Quería enrutar solo el puerto TCP 25 (SMTP) a través de una interfaz OpenVPN tap0, mientras enrutaba el resto del tráfico (incluso para el mismo host) a través de la interfaz predeterminada.

Para hacerlo, tuve que marcar los paquetes y establecer reglas para manejarlo. Primero, agregue una regla que haga que los paquetes de ruta del kernel se marquen con la 2tabla through 3(explicada más adelante):

ip rule add fwmark 2 table 3

Podrías haberle agregado un nombre simbólico /etc/iproute2/rt_tables, pero no me molesté en hacerlo. El número 2y 3se eligen al azar. De hecho, estos pueden ser los mismos, pero para mayor claridad, no lo hice en este ejemplo (aunque lo hago en mi propia configuración).

Agregue una ruta para redirigir el tráfico a través de una interfaz diferente, suponiendo que la puerta de enlace sea 10.0.0.1:

ip route add default via 10.0.0.1 table 3

¡Muy importante! Vacíe su caché de enrutamiento, de lo contrario no recibirá una respuesta y se sentará con las manos en el cabello durante algunas horas:

ip route flush cache

Ahora, establezca una regla de firewall para marcar los paquetes designados:

iptables -t mangle -A OUTPUT -p tcp --dport 465 -j MARK --set-mark 2

La regla anterior se aplica solo si los paquetes provienen de la máquina local. Ver http://inai.de/images/nf-packet-flow.png . Ajústalo a tus necesidades. Por ejemplo, si solo desea enrutar el tráfico HTTP saliente a través de la tap0interfaz, cambie 465 a 80.

Para evitar que los paquetes enviados tap0reciban su dirección LAN como IP de origen, use la siguiente regla para cambiarla a la dirección de su interfaz (suponiendo 10.0.0.2que sea la dirección IP para la interfaz tap0):

iptables -t nat -A POSTROUTING -o tap0 -j SNAT --to-source 10.0.0.2

Finalmente, relaje la validación de fuente de ruta inversa. Algunos sugieren que lo configure 0, pero 2parece una mejor opción de acuerdo con https://www.kernel.org/doc/Documentation/networking/ip-sysctl.txt . Si omite esto, recibirá paquetes (esto se puede confirmar usando tcpdump -i tap0 -n), pero los paquetes no se aceptan. El comando para cambiar la configuración para que los paquetes sean aceptados:

sysctl -w net.ipv4.conf.tap0.rp_filter=2
Lekensteyn
fuente
¿Por qué usas SNAT en lugar de MASQUERADE? ¿MASQUERADE no hace lo mismo, excepto que la dirección IP se obtiene de la interfaz en "tiempo de ejecución" y no se codifica en el archivo de configuración?
Ethan
66
@Ethan From man iptables: solo debe usarse con conexiones IP asignadas dinámicamente (marcado): si tiene una dirección IP estática, debe usar el objetivo SNAT. El enmascaramiento es equivalente a especificar una asignación a la dirección IP de la interfaz por la que sale el paquete, pero también tiene el efecto de que las conexiones se olvidan cuando la interfaz deja de funcionar. Tiene razón, pero como mi IP nunca cambiaría, decidí usar SNAT por recomendación de la página de manual.
Lekensteyn 01 de
Creo (aunque no probado) el último paso (validación de fuente de ruta inversa) no es necesario. Simplemente puede agregar otra regla a la PREROUTINGcadena a DNATla dirección de origen correcta (de acuerdo con su puerta de enlace predeterminada).
Christian Wolf
7

Resolví esto. El problema estaba en las reglas de enrutamiento de la tabla 11. La tabla 11 estaba siendo golpeada, pero las reglas de enrutamiento hacen que no funcione. Este script es lo que ahora uso, y parece funcionar bien (aunque obviamente es específico de mi configuración). Además, creé una nueva tabla 21 dedicada al enlace ascendente principal (eth1).

# Add relevant iptables entries.
iptables -t mangle -A OUTPUT -m owner --uid-owner 1002 -j MARK --set-mark 11
iptables -t nat -A POSTROUTING -o tun0 -j MASQUERADE

# Flush ALL THE THINGS.
ip route flush table main
ip route flush table 11
ip route flush table 21
ip rule flush

# Restore the basic rules and add our own.
ip rule add lookup default priority 32767
ip rule add lookup main priority 32766
ip rule add fwmark 11 priority 1000 table 11
# This next rule basically sends all other traffic down eth1.
ip rule add priority 2000 table 21

# Restore the main table.  I flushed it because OpenVPN does weird things to it.
ip route add 127.0.0.0/8 via 127.0.0.1 dev lo
ip route add 192.168.1.0/24 dev eth1 src 192.168.1.73
ip route add default via 192.168.1.254

# Set up table 21.  This sends all traffic to eth1.
ip route add 192.168.1.0/24 dev eth1 table 21
ip route add default via 192.168.1.254 dev eth1 table 21

# Set up table 11.  I honestly don't know why 'default' won't work, or
# why the second line here is needed.  But it works this way.
ip route add 10.32.0.49/32 dev tun0 table 11
ip route add 10.32.0.1 via 10.32.0.50 dev tun0 table 11
ip route add 0.0.0.0/1 via 10.32.0.50 dev tun0 table 11

ip route flush cache

## MeanderingCode edit (porque todavía no puedo comentar)

Gracias por esta respuesta! Parece que esto podría ser complicado, ya que tendrías que mantener la información de la ruta aquí (posiblemente duplicando o rompiendo otras cosas que pueden querer establecer rutas).

Puede estar experimentando "cosas raras" en su tabla de enrutamiento de OpenVPN porque el servidor está configurado para "enviar" rutas, permitiendo que todo el tráfico se enrute a través de la interfaz de red VPN, en lugar de Internet "desnudo". O su configuración OpenVPN o cualquier script / aplicación que lo configure está configurando rutas.

En el primer caso, se puede editar la configuración de OpenVPN y poner en una línea que contiene "ruta-nopull"
En este último, la configuración de verificación para OpenVPN o cualquier envoltorio (network-manager-openvpn, por ejemplo, en muchas distribuciones Linux de escritorio actuales)
en En cualquier caso, eliminar la configuración de enrutamiento donde se establece es más limpio y seguro que vaciar la tabla, dependiendo de cuándo ejecute este script y qué más esté haciendo su sistema.

¡Aclamaciones!

Ethan
fuente
2

Creo que quieres:

/sbin/ip route add default via 10.32.0.49 dev tun0 table 11
/sbin/ip rule add priority 10000 fwmark 11 table 11

http://lartc.org/howto/lartc.netfilter.html

Pero no he probado lo anterior.

mfarver
fuente
El primer comando devuelve "Error:" a "está duplicado o" 10.32.0.49 "es una basura".
Ethan
Lo siento, olvidé la vía entre el valor predeterminado y la IP.
mfarver
Lo intenté. Parece tener el mismo efecto que dejar fuera la defaultpalabra clave.
Ethan
Para eliminar las opciones, puede intentar hacer una captura de paquetes en tun0 y ver si los paquetes terminan en esa interfaz. tcpdump -i tun0 .. alternativamente, ¿necesita basarlo en la ID de usuario? ¿Podría hacerlo según la dirección IP de destino (más fácil)? route add <host remoto> a través de 10.32.0.49
mfarver
No puedo basar si fuera de la dirección IP de destino, habría muchos de esos. Teóricamente, también podría basarlo en pid, pero eso es aún más difícil, porque no conoceré el pid hasta que comiencen los demonios.
Ethan
0

Esto se puede hacer sin el comando iptables Ejecutar el comando ip simple

Para uid 1002:

ip rule add uidrange 1002-1002 table 502

Agregue la ruta predeterminada para la tabla 502 sobre la interfaz que desea decir

eth0 ip rule add default via a.b.c.d dev eth0
jainendra
fuente
Error: argument "uidrange" is wrong: Failed to parse rule type
Probé
@kasperd debes haberlo estado haciendo mal, porque funciona bien para mí.
horseyguy