Cerrar manualmente un puerto desde la línea de comandos

112

Quiero cerrar un puerto abierto que esté en modo de escucha entre mi cliente y la aplicación del servidor.

¿Hay alguna opción de línea de comando manual en Linux para cerrar un puerto?

NOTA: Llegué a saber que "solo la aplicación que posee el socket conectado debe cerrarlo, lo que sucederá cuando la aplicación finalice".

No entiendo por qué solo es posible mediante la aplicación que lo abre ... Pero todavía estoy ansioso por saber si hay otra forma de hacerlo.

Glorfindel
fuente
55
No, los puertos abiertos pertenecen al proceso que los abrió, no hay control posible desde el exterior. Lo que es bueno, o todas las aplicaciones tendrían que anticipar que sus puertos abiertos (y archivos) se alterarán. Sin embargo, puede bloquear el tráfico a un puerto cortando firewalls (iptables), pero eso no se cerrará y dejará el puerto para otro uso.
Jürgen Strobel
10
Muchos de los que respondieron perdieron el punto de esta pregunta. No tiene sentido declarar que solo la aplicación propietaria del puerto puede desconectarlo. ¡Puedo desconectarlo caminando hacia la caja y sacando el cable de ethernet del enchufe, o matando la aplicación en el otro extremo de la conexión! La aplicación debe estar escrita para manejar esto. Entonces, ¿cómo se prueba para asegurarse de que la aplicación esté escrita correctamente sin requerir intervención física y / o control de la otra computadora?
Dale Wilson
"... no hay control posible desde afuera". Esa es una observación importante, que me guió a la siguiente pregunta, ¿cómo puedo ser parte del proceso desde afuera? GDB.
Dankó Dávid
@ JürgenStrobel De hecho, existe un control posible desde el exterior: tanto tcpkill como ss pueden hacer exactamente lo que se les pide. Porque los puertos abiertos no pertenecen realmente a un proceso; son recursos del kernel, con algunos derechos asignados a un proceso, pero que aún existen solo a gusto del kernel.
Tom Anderson
@ tom-anderson DaleW: tcpkill es una herramienta de firewall y mencioné esta opción. Puede evitar el tráfico a un puerto, que es diferente de cerrar un puerto (socket).
Jürgen Strobel

Respuestas:

134

Tuve el mismo problema, el proceso debe mantenerse vivo pero el zócalo debe cerrarse. Cerrar un socket en un proceso en ejecución no es imposible pero difícil:

  1. ubica el proceso:

    netstat -np
    

    Obtienes un source/destination ip:port portstate pid/processnamemapa

  2. localizar el descriptor de archivo del socket en el proceso

    lsof -np $pid
    

    Obtiene una lista: nombre de proceso, pid, usuario, fileDescriptor, ... una cadena de conexión.

    Localice el número de descriptor de archivo correspondiente para la conexión. Será algo así como "97u", que significa "97".

  3. Ahora conecta el proceso:

    gdb -p $pid
    
  4. Ahora cierre el zócalo:

    call close($fileDescriptor) //does not need ; at end.
    

    ejemplo:

    call close(97)
    

    Luego separe gdb:

    quit
    

    Y el zócalo está cerrado.

Dankó Dávid
fuente
1
sudo lsof -np $pidme da unas 200 líneas y estoy confundido acerca de cómo encontrar el FD deseado. En mi caso, el proceso es una pestaña de Chrome y estoy tratando de cerrar los websockets abiertos ...
SET
2
Una fila normalmente se parece a: firefox 14812 szupervigyor 97u IPv4 32814564 0t0 TCP 192.168.2.4:40385->173.194.39.65:https (ESTABLECIDO) como: nombre_proceso usuario usuario fd [abierto_para] protocolo dispositivo inodo protocolo_data_toString
Dankó Dávid
2
* Una fila normalmente se ve así: firefox 14812 szupervigyor 97u IPv4 32814564 0t0 TCP 192.168.2.4:40385->173.194.39.65:https (ESTABLECIDO) como: process_name pid user fd [abrir_para] dispositivo de protocolo inode protocol_data_toString Necesita conocer la ip remota dirección y encontrar en el último col. En mi ejemplo, el 97 es el FileDescriptor. La búsqueda es difícil si abrió múltiples conexiones al host de destino.
Dankó Dávid
77
esta es una solución brillante
marcorossi
8
Si desea simular la toma de ser cerrado por el extremo remoto (por ejemplo, el par de salir) es mejor utilizar shutdown: call shutdown($fileDescriptor, 0).
ecatmur
75

Estás haciendo una pregunta equivocada aquí. Realmente no es posible simplemente "cerrar un puerto" desde fuera de la aplicación que abrió el socket escuchando en él. La única forma de hacerlo es eliminar por completo el proceso que posee el puerto. Luego, en aproximadamente un minuto o dos, el puerto volverá a estar disponible para su uso. Esto es lo que está sucediendo (si no te importa, salta al final donde te muestro cómo matar el proceso que posee un puerto en particular):

Los puertos son recursos asignados por el sistema operativo a diferentes procesos. Esto es similar a pedirle al sistema operativo un puntero de archivo. Sin embargo, a diferencia de los punteros de archivo, solo UN proceso a la vez puede tener un puerto. A través de la interfaz de socket BSD, los procesos pueden hacer una solicitud para escuchar en un puerto, que luego el sistema operativo otorgará. El sistema operativo también se asegurará de que ningún otro proceso tenga el mismo puerto. En cualquier momento, el proceso puede liberar el puerto cerrando el socket. El sistema operativo reclamará el puerto. Alternativamente, si el proceso finaliza sin liberar el puerto, el sistema operativo finalmente recuperará el puerto (aunque no sucederá de inmediato: tomará unos minutos).

Ahora, lo que desea hacer (simplemente cierre el puerto desde la línea de comandos), no es posible por dos razones. Primero, si fuera posible, significaría que un proceso simplemente podría robar el recurso de otro proceso (el puerto). Esta sería una mala política, a menos que esté restringida a procesos privilegiados. La segunda razón es que no está claro qué pasaría con el proceso que poseía el puerto si dejamos que continúe ejecutándose. El código del proceso se escribe asumiendo que posee este recurso. Si simplemente lo quitáramos, terminaría fallando por sí mismo, por lo que los sistemas operativos no le permiten hacer esto, incluso si es un proceso privilegiado. En cambio, simplemente debes matarlos.

De todos modos, aquí se explica cómo matar un proceso que posee un puerto en particular:

sudo netstat -ap | grep :<port_number>

Eso generará la línea correspondiente al puerto de retención del proceso, por ejemplo:

tcp  0  0 *:8000   *:* LISTEN  4683/procHoldingPort

En este caso, procHoldingPort es el nombre del proceso que abrió el puerto, 4683 es su pid y 8000 (tenga en cuenta que es TCP) es el número de puerto que posee.

Luego, mira en la última columna, verás /. Entonces ejecute esto:

kill  <pid>

Si eso no funciona (puede verificarlo volviendo a ejecutar el comando netstat). Hacer esto:

kill -9 <pid>

En general, es mejor evitar enviar SIGKILL si puedes. Por eso te digo que lo intentes killantes kill -9. Solo usando killenvía la SIGTERM más suave.

Como dije, aún se necesitarán unos minutos para que el puerto se vuelva a abrir si haces esto. No sé una manera de acelerar esto. Si alguien más lo hace, me encantaría escucharlo.

Guy Avraham
fuente
@smehmood - Gracias por la explicación detallada ... Una pequeña duda ... ¿Cómo reclama el núcleo un puerto abierto por un proceso abruptamente eliminado? .. la solución u siempre parece estar matando el proceso que tiene el puerto ...
3
@codingfreak El kernel sabe que el proceso se ha ido. Sabe que puede reclamar el puerto. En realidad, hay reglas sobre el momento de cerrar el puerto, para asegurarse de que no haya paquetes extraviados flotando en la red. ¿Cómo sabe que tiene estos recursos? eso es lo que hace el núcleo, hace un seguimiento de las cosas.
Rich Homolka el
Hasta que alguien publique algo sensato como unix.tools.port.close(<my port number>)yo usaría init 6.
Snowcrash
Esta es una excelente respuesta a una pregunta diferente. Esta pregunta se trata de adjuntar un depurador y cambiar un programa en ejecución para que ese programa invoque una función en un descriptor de archivo.
Arthur Ulfeldt
19

El fusor también se puede usar

fuser -k -n *protocol portno*

Aquí el protocolo es tcp / udp y portno es el número que desea cerrar. P.ej

fuser -k -n tcp 37

Más información en la página de manual de fuser

Barón Rojo
fuente
Solo matar el proceso de propiedad no funcionó para mí, pero el fusor sí. ¡Gracias!
webwurst
Obtuve resultados mixtos con él, incluso después de usar el fusor, obtuve el "socket ya en uso" al intentar reutilizar el puerto de la aplicación eliminada, incluso haciéndolo como root. Por otro lado, el tiempo para liberar el zócalo parecía ser más corto que antes, así que gracias de todos modos.
Jan Vlcinsky
@ JanVlcinsky ¿Tal vez hay un proceso "guardián" que reinicia el proceso finalizado momentos después de fuserejecutarse?
RedBaron
@RedBaron: según el comentario superuser.com/a/415236/153413, mi problema se origina en una aplicación mal escrita, que no limpia / cierra el zócalo bajo la terminación. Entonces fuserencuentra el proceso usando el puerto y lo mata, pero no resuelve el hecho de que el socket no está cerrado. 60 segundos y el núcleo lo hace por mí.
Jan Vlcinsky
6

Alternativamente, podría usar iptables:

iptables -I INPUT -p tcp --dport 80 -j DROP

Básicamente logra lo que quieres. Esto soltará todo el tráfico TCP al puerto 80.

supercheetah
fuente
44
No, esto mantendrá el socket abierto hasta que todos los tiempos de espera los cierren. (Ocultará todo el tráfico del proceso de propiedad que luego no tiene medios para saber que debería cerrarlo). Puede usar -j REJECTpara devolver el indicador de restablecimiento de TCP, que luego se vería en mi proceso de propiedad (pero solo cuando el otro lado lo intente para enviar algo).
Marki555
3
netstat -anp | grep 80

Debería decirle, si está ejecutando apache, "httpd" (esto es solo un ejemplo, use el puerto que usa su aplicación en lugar de 80)

pkill -9 httpd 

o

killall -9 httpd
Ben
fuente
44
intente una matanza normal antes de recurrir a -9
Thilo
@omfgroflmao - ¿Pero matará el proceso que ha abierto el puerto?
@codingfreak El proceso que mantiene el puerto, sí, lo matará.
2

Probablemente podría averiguar qué proceso abrió el socket con el que está asociado el puerto y eliminar ese proceso.

Pero, tendría que darse cuenta de que a menos que ese proceso tenga un controlador que inicialice todas las cosas que estaba usando (archivos abiertos, tomas, horquillas, cosas que pueden persistir a menos que se cierre correctamente al finalizar), entonces habría creado eso arrastre el rendimiento del sistema. Además, el socket permanecerá abierto hasta que el kernel se dé cuenta de que el proceso ha finalizado. Eso usualmente solo toma alrededor de un minuto.

Supongo que la mejor pregunta sería: ¿Qué puerto (que pertenece a qué proceso) desea detener?

Si está tratando de poner fin a una puerta trasera o un virus que encontró, al menos debe saber qué datos van y vienen antes de finalizarlos. (Wirehark es bueno para esto) (Y el nombre ejecutable del proceso para que pueda eliminarlo y evitar que vuelva a reiniciarse) o, si es algo que instaló (como HTTPD o FTPD o algo), entonces ya debería tener acceso a El proceso en sí.

Por lo general, tendrá un programa de control (HTTPD stop | start o algo así). O, si es algo del sistema, probablemente no deberías meterte con él. De todos modos, pensé que dado que todos los demás te están dando el ángulo de "cómo hacerlo", debería darte las advertencias.

Jackenheimer
fuente
Muy buen comentario. Tengo un programa que, al finalizar, no cierra el zócalo, lo que da como resultado el comportamiento descrito: el zócalo no se puede usar durante aproximadamente 60 segundos. Cuando detengo e inicio ese proceso, se queja durante aproximadamente un minuto, de que la dirección y el puerto ya están en uso. La mejor solución es corregir el proceso de mal comportamiento para cerrarlo correctamente, pero a veces esto no es una opción. ¿Hay alguna forma de pedirle al kernel que compruebe ese socket bloqueado antes de 60 segundos?
Jan Vlcinsky
2

Primero busqué los procesos mongo y nodo, luego hice lo siguiente:

ps -A | grep node

10418 pts/23   00:00:05 node

10551 pts/23   00:00:00 node

ps -A | grep mongo

10490 pts/23   00:00:00 mongod

Una vez identificado, simplemente elimine los procesos con el comando kill.

kill -9 10418
kill -9 10490

Por último, escriba meteory debería estar funcionando nuevamente.

abautista
fuente
1

Puede escribir un script que modifique las tablas ip y las reinicie. Un script para agregar una regla que descarte todos los paquetes en el puerto, otro script para eliminar dicha regla.

Las otras respuestas le han mostrado cómo matar el proceso vinculado al puerto; esto puede no ser lo que desea. Si desea que el servidor siga ejecutándose, pero para evitar conexiones de clientes, entonces desea bloquear el puerto, no detener el proceso.

Michael Shimmins
fuente
@Michael Shimmins ... hmm suena interesante ya que podemos bloquear el puerto en el lado del servidor para que el cliente no envíe ningún mensaje.
Bueno, el cliente puede enviar mensajes todo lo que quiera. Acabamos de cerrar la puerta para que no puedan entrar.
1

Un problema más: en algún momento el núcleo posee puertos propios. Sé que el enrutamiento NAT mantiene algunos puertos abiertos para uso NAT. No puede eliminar un proceso para esto, este es un núcleo, y se requiere una reconfiguración y un reinicio.

Rich Homolka
fuente
1

Si desea que su puerto se libere antes, debe establecer el siguiente valor:

echo 1 > /proc/sys/net/ipv4/tcp_fin_timeout

para configurarlo de 60 segundos (predeterminado) a 1 segundo

Daniel
fuente
1

Puede usar el comando llamado killcx, cerrando una conexión sin que se elimine ningún proceso.

  • sintaxis:
killcx [dest_ip:dest_port] {interface}

  dest_ip              : remote IP
  dest_port            : remote port
  interface (optional) : network interface (eth0, lo etc).
  • ejemplo:
killcx 120.121.122.123:1234
killcx 120.121.122.123:1234 eth0
shuaiming
fuente
// ¿La mayoría de las máquinas Linux tienen killcx? Ninguno de los que he probado tiene este comando killcx disponible sin instalar paquetes que no están disponibles para ellos con su configuración de repositorio actual.
Nathan Basanese
no, puedes encontrar la herramienta aquí: killcx.sourceforge.net
shuaiming
//, sé que puedo encontrar la herramienta, es difícil instalarla en miles de servidores.
Nathan Basanese
Use Puppet @NathanBasanese :)
SHOUBHIK BOSE
1

Soy consciente de que esta respuesta no responde a la pregunta en sí misma, estrictamente hablando, pero leerla podría ser información relevante:

El comportamiento predeterminado de vincular un socket a un puerto (y dirección) es que cuando el socket se cierra por la terminación abrupta del proceso, el socket permanecerá en TIME_WAIT por un tiempo. Esto significa que no puede volver a vincular inmediatamente a esta dirección / puerto. Si está desarrollando el sistema en sí a través de la interfaz de socket BSD estándar, puede (al menos hasta cierto punto) controlar este comportamiento con la opción de socket SO_REUSEADDR. Básicamente, esto le permitirá vincularse a la misma dirección / puerto nuevamente si el socket está en estado TIME_WAIT. ¡Todavía hay un zócalo por puerto!

Sin embargo, esta información solo debe usarse como ayuda para el desarrollo, ya que hay una razón para que TIME_WAIT exista en primer lugar, ya explicada en las otras respuestas.

Tommi Tuura
fuente
// , Derecha. Por supuesto, matar procesos no es la mejor manera de liberar puertos. He notado que cuando mato el proceso de Vagrant si se cuelga, no puedo volver a vagabundear por un tiempo. Apuesto a que esto TIME_WAIT tiene algo que ver con eso.
Nathan Basanese
0

Si no desea tráfico a través del socket pero quiere mantener vivo el proceso: tcpkill .

tcpkill -i eth0 host xxx.xxx.xxx.xxx and port yyyy

Comenzará un demonio de rastreo que intercepta y destruye todo el tráfico en ese puerto. Muy útil para probar sus aplicaciones para divisiones de red.

RickyA
fuente
0

Puede cerrar una toma de escucha con ss :

sudo ss --kill state listening src :1234

Donde 1234 es su número de puerto.

ss es parte del paquete iproute2, por lo que hay un fuerte cambio, ya está instalado en un Linux moderno.

Aprendí sobre esto de una respuesta a una pregunta relacionada .

Tom Anderson
fuente