Son las cinco y media de la tarde en el SOC y salta una alerta de volumen de salida: srv-bodega, el servidor de la Bodega Viña Alta, lleva horas mandando muchos megas por hora a una IP de fuera que no es de nadie de su ficha. En ese servidor está su tienda online, que cobra por una pasarela de pagos y manda los pedidos a la distribuidora.
No sabes quién hay al otro lado ni qué es lo que sale, y para contener no hace falta saberlo: lo que sale son datos que la bodega pierde, y cada minuto cuenta. Contener es cortar esa salida sin cortar el negocio. Apagar el servidor contiene, pero la tienda deja de vender; cerrar todo lo que sale por el 443 también, pero los pagos no salen. Lo que se busca es cortar justo lo que no debería salir, y comprobar que de verdad ha dejado de salir.
Desde el SOC entras en el servidor por SSH. Su ficha dice qué es de la bodega:
tecnico@srv-bodega:~$ cat ~/ficha-bodega.txt Bodega Viña Alta · srv-bodega (192.168.88.10) Ficha del SOC, revisada el 2 de octubre de 2026 Lo que sirve: la tienda online, en el 443 (y el 80, que lleva al 443) Lo que sale (todo al 443), con la cuenta tienda: 192.0.2.40 la pasarela de pagos (programa pagos) 198.51.100.20 la distribuidora, con los pedidos (programa pedidos) Nada más tiene que salir. Si se corta algo de esto, la tienda no cobra o los pedidos no llegan: avisar a la bodega antes de tocarlo.
Qué sale y hacia dónde
ss sin -l enseña las conexiones establecidas (con -l, lo que escucha; -t, solo TCP; -n,
con números en vez de nombres):
tecnico@srv-bodega:~$ ss -tn State Recv-Q Send-Q Local Address:Port Peer Address:Port Process ESTAB 0 0 192.168.88.10:33027 198.51.100.20:443 ESTAB 0 0 192.168.88.10:45886 192.0.2.40:443 ESTAB 0 0 192.168.88.10:56059 198.51.100.194:443
Tres conexiones al 443. Dos están en la ficha; la tercera, a 198.51.100.194, no (en tu partida será
otra IP). Con -p, de qué programa es cada una. Son de la cuenta de la tienda, así que sin sudo,
ss no lo dice:
tecnico@srv-bodega:~$ sudo ss -tnp State Recv-Q Send-Q Local Address:Port Peer Address:Port Process ESTAB 0 0 192.168.88.10:33027 198.51.100.20:443 users:(("pedidos",pid=401,fd=3)) ESTAB 0 0 192.168.88.10:45886 192.0.2.40:443 users:(("pagos",pid=402,fd=3)) ESTAB 0 0 192.168.88.10:56059 198.51.100.194:443 users:(("sincroniza",pid=403,fd=3))
Y cuánto lleva: -i añade lo que sabe TCP de cada conexión, y bytes_acked es lo que ya ha llegado
al otro lado. Dos veces seguidas:
tecnico@srv-bodega:~$ ss -tni dst 198.51.100.194 | grep -o 'bytes_acked:[0-9]*' bytes_acked:2138675001
tecnico@srv-bodega:~$ ss -tni dst 198.51.100.194 | grep -o 'bytes_acked:[0-9]*' bytes_acked:2138812501
Más de dos gigas, y sigue creciendo. ps cuenta el resto: con qué cuenta corre el programa, cuánto
lleva en marcha (ELAPSED) y qué es:
tecnico@srv-bodega:~$ ps -o user,pid,etime,args -p 403 USER PID ELAPSED COMMAND tienda 403 04:19:16 /usr/local/bin/sincroniza
Ejercicio
Lo que sale: ss -tnp, ss -ti y ps
srv-bodega manda mucho a una IP que no está en la ficha (~/ficha-bodega.txt). Encuentra la conexión, cuánto lleva enviado y de qué programa es.
Preparando la máquina…
Cortarla en el servidor
ufw también corta lo que sale: deny out to IP (o reject out to IP, que contesta «Connection
refused» al momento en vez de dejar esperando). Las de salida salen aparte en ufw status, con
DENY OUT:
tecnico@srv-bodega:~$ sudo ufw deny out to 198.51.100.194 Rule added
tecnico@srv-bodega:~$ sudo ufw status Status: active To Action From -- ------ ---- OpenSSH ALLOW Anywhere 80/tcp ALLOW Anywhere 443/tcp ALLOW Anywhere OpenSSH (v6) ALLOW Anywhere (v6) 80/tcp (v6) ALLOW Anywhere (v6) 443/tcp (v6) ALLOW Anywhere (v6) 198.51.100.194 DENY OUT Anywhere
Pero mira lo que lleva enviado:
tecnico@srv-bodega:~$ ss -tni dst 198.51.100.194 | grep -o 'bytes_acked:[0-9]*' bytes_acked:2139362501
Sigue creciendo. Una regla nueva corta las conexiones nuevas; la que ya iba, no.
Quien recuerda las conexiones establecidas es conntrack, el seguimiento de conexiones del núcleo (la herramienta viene en el paquete conntrack; en srv-bodega la puso el SOC). Su entrada:
tecnico@srv-bodega:~$ sudo conntrack -L -d 198.51.100.194 tcp 6 431999 ESTABLISHED src=192.168.88.10 dst=198.51.100.194 sport=56059 dport=443 src=198.51.100.194 dst=192.168.88.10 sport=443 dport=56059 [ASSURED] mark=0 use=1 conntrack v1.4.6 (conntrack-tools): 1 flow entries have been shown.
Si se borra, el siguiente paquete de esa conexión pasa por las reglas como si fuera nuevo, y la regla lo corta:
tecnico@srv-bodega:~$ sudo conntrack -D -d 198.51.100.194 tcp 6 431999 ESTABLISHED src=192.168.88.10 dst=198.51.100.194 sport=56059 dport=443 src=198.51.100.194 dst=192.168.88.10 sport=443 dport=56059 [ASSURED] mark=0 use=1 conntrack v1.4.6 (conntrack-tools): 1 flow entries have been deleted.
tecnico@srv-bodega:~$ ss -tn dst 198.51.100.194 State Recv-Q Send-Q Local Address:Port Peer Address:Port Process ESTAB 0 99912 192.168.88.10:56059 198.51.100.194:443
La conexión sigue ahí (ESTAB), pero lo que el programa manda se queda en su cola sin salir: Send-Q,
llena (99912 bytes), y bytes_acked ya no crece. Otra forma es cerrarla: ss -K, como root, cierra
las conexiones que casan con el filtro (el programa sigue vivo):
tecnico@srv-bodega:~$ sudo ss -K dst 198.51.100.194 Netid State Recv-Q Send-Q Local Address:Port Peer Address:Port Process tcp ESTAB 0 99912 192.168.88.10:56059 198.51.100.194:https
Matar el programa también la corta, pero se lleva lo que enseña (el proceso, lo que tenía abierto) a quien venga después a investigar; y si algo lo vuelve a lanzar, vuelve a salir. Lo que no vuelve a salir, con la regla puesta, es una conexión nueva hacia esa IP.
Para que nadie lo vuelva a lanzar sin borrarlo (es una prueba), se le quita el permiso de ejecución:
sudo chmod a-x /usr/local/bin/sincroniza; lo que ya corre sigue igual, pero ya no se puede volver a
arrancar. Y si alguien tiene que analizarlo, mejor una copia aparte (cp), también sin ejecución, y
el original donde está.
Ejercicio
Cortar la salida en ufw (y lo que ya iba)
Corta en ufw la salida hacia la IP que no está en la ficha, sin tocar los pagos ni los pedidos. Después, comprueba si de verdad ha dejado de salir.
Preparando la máquina…
No cerrar el grifo entero
El primer impulso suele ser cerrar todo. Es lo que hizo alguien por la mañana en otro turno: todo el 443 de salida, cerrado.
tecnico@srv-bodega:~$ sudo ufw status Status: active To Action From -- ------ ---- OpenSSH ALLOW Anywhere 80/tcp ALLOW Anywhere 443/tcp ALLOW Anywhere OpenSSH (v6) ALLOW Anywhere (v6) 80/tcp (v6) ALLOW Anywhere (v6) 443/tcp (v6) ALLOW Anywhere (v6) 443/tcp DENY OUT Anywhere 443/tcp (v6) DENY OUT Anywhere (v6)
Cada compra abre una conexión nueva con la pasarela de pagos, y ahora no sale:
tecnico@srv-bodega:~$ nc -zv -w 3 192.0.2.40 443 nc: connect to 192.0.2.40 port 443 (tcp) timed out: Operation now in progress
Y lo que ya iba hacia 198.51.100.194, por lo mismo de antes, sigue saliendo:
tecnico@srv-bodega:~$ ss -tni dst 198.51.100.194 | grep -o 'bytes_acked:[0-9]*' bytes_acked:2138537501
Tira el negocio y ni siquiera corta. Hay que dejarlo en una regla solo para esa IP. ufw status numbered numera las reglas (las de salida llevan (out)):
tecnico@srv-bodega:~$ sudo ufw status numbered Status: active To Action From -- ------ ---- [ 1] OpenSSH ALLOW IN Anywhere [ 2] 80/tcp ALLOW IN Anywhere [ 3] 443/tcp ALLOW IN Anywhere [ 4] 443/tcp DENY OUT Anywhere (out) [ 5] OpenSSH (v6) ALLOW IN Anywhere (v6) [ 6] 80/tcp (v6) ALLOW IN Anywhere (v6) [ 7] 443/tcp (v6) ALLOW IN Anywhere (v6) [ 8] 443/tcp (v6) DENY OUT Anywhere (v6) (out)
ufw delete con la regla escrita quita la de IPv4 y la de IPv6 de una vez (con el número, solo esa):
tecnico@srv-bodega:~$ sudo ufw delete deny out 443/tcp Rule deleted Rule deleted (v6)
tecnico@srv-bodega:~$ sudo ufw reject out to 198.51.100.194 Rule added
Y se comprueba lo que importa: el pago nuevo sale y la IP de fuera no.
tecnico@srv-bodega:~$ nc -zv -w 3 192.0.2.40 443 Connection to 192.0.2.40 443 port [tcp/https] succeeded!
tecnico@srv-bodega:~$ nc -zv -w 3 198.51.100.194 443 nc: connect to 198.51.100.194 port 443 (tcp) failed: Connection refused
Ejercicio
No cerrar el grifo entero: ufw status numbered y delete
Por la mañana alguien cerró en ufw todo el 443 de salida para cortar esa IP. Mira qué ha roto y déjalo en una regla solo para ella.
Preparando la máquina…
En el router, para toda la red
srv-bodega no es el único equipo de la bodega. El router ve lo que sale de todos, y una regla en él corta para toda la red. Es un MikroTik (R-Bodega): el operador en ether1 y el servidor en ether2. Torch enseña lo que pasa por un puerto en ese momento, conexión a conexión:
[admin@R-Bodega] > /tool torch interface=ether2 duration=2s -- [Q quit|D dump|C-z pause]Columns: MAC-PROTOCOL, IP-PROTOCOL, DSCP, SRC-ADDRESS, SRC-PORT, DST-ADDRESS, DST-PORT, TX MA IP- DSCP SRC-ADDRESS SRC-PORT DST-ADDRESS DST-PORT TX ip tcp 0 192.168.88.10 56059 198.51.100.194 443 (https) 44.3kbps ip tcp 0 192.168.88.10 45886 192.0.2.40 443 (https) 9.0kbps ip tcp 0 192.168.88.10 33027 198.51.100.20 443 (https) 3.0kbps -- [Q quit|D dump|C-z pause]
[admin@R-Bodega] > /ip firewall connection print Flags: S - SEEN-REPLY; A - ASSURED; C - CONFIRMED; F - FASTTRACK; s - SRCNAT Columns: PROTOCOL, SRC-ADDRESS, SRC-PORT, DST-ADDRESS, DST-PORT, TCP-STATE, TIMEOUT # PRO SRC-ADDRESS SRC-P DST-ADDRESS DST TCP-STATE TIMEOUT 0 SACFs tcp 192.168.88.10 33027 198.51.100.20 443 established 23h59m59s 1 SACFs tcp 192.168.88.10 45886 192.0.2.40 443 established 23h59m59s 2 SACFs tcp 192.168.88.10 56059 198.51.100.194 443 established 23h59m59s
where filtra, y detail enseña todo de una: orig-bytes es lo que ha salido por esa conexión y
orig-rate, a qué ritmo sale ahora.
[admin@R-Bodega] > /ip firewall connection print detail where dst-address~"198.51.100.194" Flags: S - SEEN-REPLY; A - ASSURED; C - CONFIRMED; F - FASTTRACK; s - SRCNAT 2 SACFs protocol=tcp src-address=192.168.88.10 src-port=56059 dst-address=198.51.100.194 dst-port=443 reply-src-address=198.51.100.194 reply-src-port=443 reply-dst-address=203.0.113.2 reply-dst-port=56059 tcp-state=established timeout=23h59m59s orig-packets=1 478 031 orig-bytes=2 140 187 500 orig-fasttrack-packets=0 orig-fasttrack-bytes=0 repl-packets=26 875 repl-bytes=38 912 500 repl-fasttrack-packets=0 repl-fasttrack-bytes=0 orig-rate=1100.0kbps repl-rate=20.0kbps
Ejercicio
Lo que sale de la red: Torch y la tabla de conexiones
Desde el router de la bodega, mira qué sale de su red y hacia dónde. En la ficha solo están la pasarela de pagos (192.0.2.40) y la distribuidora (198.51.100.20): busca la conexión que manda mucho a otra IP.
Encendiendo el router…
La regla, delante de lo establecido
En el router pasa lo mismo que en el servidor. Una regla de corte añadida al final de forward:
[admin@R-Bodega] > /ip firewall filter add chain=forward dst-address=198.51.100.194 action=drop comment="Salida rara"
[admin@R-Bodega] > /ip firewall filter print stats Flags: D - DYNAMIC Columns: CHAIN, ACTION, BYTES, PACKETS # CHAIN ACTION BYTES PACKETS ;;; special dummy rule to show fasttrack counters 0 D forward passthrough 0 0 ;;; defconf: fasttrack 1 forward fasttrack-connection 2 239 968 750 1 546 956 ;;; defconf: accept established,related, untracked 2 forward accept 2 239 968 750 1 546 956 ;;; defconf: drop invalid 3 forward drop 0 0 ;;; defconf: drop all from WAN not DSTNATed 4 forward drop 0 0 ;;; Salida rara 5 forward drop 0 0
Cero. La conexión ya establecida la deja pasar antes la regla 2, «accept established», y nunca llega a
la del final. Hay que ponerla delante. print da los números:
[admin@R-Bodega] > /ip firewall filter print Flags: D - DYNAMIC 0 D ;;; special dummy rule to show fasttrack counters chain=forward action=passthrough 1 ;;; defconf: fasttrack chain=forward action=fasttrack-connection connection-state=established,related 2 ;;; defconf: accept established,related, untracked chain=forward action=accept connection-state=established,related,untracked 3 ;;; defconf: drop invalid chain=forward action=drop connection-state=invalid 4 ;;; defconf: drop all from WAN not DSTNATed chain=forward action=drop connection-state=new connection-nat-state=!dstnat in-interface=ether1 5 ;;; Salida rara chain=forward action=drop dst-address=198.51.100.194
La 0 la pone el propio router en cuanto hay una regla de fasttrack (D, dinámica), y delante de ella no se puede mover nada:
[admin@R-Bodega] > /ip firewall filter move [find comment="Salida rara"] destination=0 failure: cannot move builtin
[admin@R-Bodega] > /ip firewall filter move [find comment="Salida rara"] destination=1
[admin@R-Bodega] > /ip firewall filter print stats Flags: D - DYNAMIC Columns: CHAIN, ACTION, BYTES, PACKETS # CHAIN ACTION BYTES PACKETS ;;; special dummy rule to show fasttrack counters 0 D forward passthrough 0 0 ;;; Salida rara 1 forward drop 1 680 4 ;;; defconf: fasttrack 2 forward fasttrack-connection 2 242 123 750 1 548 452 ;;; defconf: accept established,related, untracked 3 forward accept 2 242 123 750 1 548 452 ;;; defconf: drop invalid 4 forward drop 0 0 ;;; defconf: drop all from WAN not DSTNATed 5 forward drop 0 0
Ahora sí: la regla está delante y sus contadores suben con lo que corta (los reintentos del servidor), y lo de esa conexión ya no llega a «accept».
Si son varias IPs, una lista: cada IP en la lista y una sola regla con dst-address-list.
[admin@R-Bodega] > /ip firewall address-list add list=bloqueadas address=198.51.100.194 comment="Salida rara"
[admin@R-Bodega] > /ip firewall address-list print Columns: LIST, ADDRESS, CREATION-TIME # LIST ADDRESS CREATION-TIME ;;; Salida rara 0 bloqueadas 198.51.100.194 2026-10-16 17:30:52
La regla que la usa se pone ya delante de lo establecido, con place-before y el número de la de
fasttrack en el último print: /ip firewall filter add chain=forward dst-address-list=bloqueadas action=drop place-before=1. Para otra IP, basta con añadirla a la lista.
Ejercicio
Cortar en el router: delante de lo establecido
Corta en el router la salida hacia la IP que no está en la ficha, para toda la red, sin tocar la pasarela de pagos (192.0.2.40) y la distribuidora (198.51.100.20). Ojo: esa conexión ya está establecida.
Encendiendo el router…
Lo que cuesta cada decisión
- Apagar srv-bodega: contiene, pero la tienda deja de vender, y se pierde lo que estaba en memoria.
- Cerrar todo el 443 de salida (en el servidor o en el router): los pagos y los pedidos dejan de salir, y lo que ya iba hacia fuera sigue saliendo.
- Una regla solo para esa IP en el servidor: contiene srv-bodega, si además se corta lo que ya
estaba establecido (conntrack,
ss -K). - La misma en el router, delante de lo establecido: contiene para toda la red.
- Y después, mirar otra vez: si el volumen sigue, hacia otra IP o desde otro equipo, la primera regla no bastaba.
En la sala: el SOC de tarde
En la sala estás en el SOC a las cinco y media, con la alerta del volumen que sale de srv-bodega en la pared y dos terminales en tu puesto: la del servidor y, en su pestaña, la consola de R-Bodega. Mientras siga saliendo, cada quince minutos la alerta vuelve con los megas perdidos; a las siete y media, si no se ha cortado, el día se cierra así. Si el programa ve su conexión cortada un rato, prueba con otra IP: hay que mirar otra vez después de cortar. Lo que tires (la tienda, los pagos, los pedidos, o tu propio SSH si aíslas el servidor) cuenta como servicio caído, y la nota mira además los datos que salieron, que lo cortes también en el router y que el programa siga vivo para quien investigue después.