Ручне закриття порту з командного рядка


112

Я хочу закрити відкритий порт, який знаходиться в режимі прослуховування між моїм клієнтом і серверною програмою.

Чи є в Linux будь-який варіант командного рядка для закриття порту?

ПРИМІТКА. Мені стало відомо, що «закривати його має лише програма, яка володіє підключеним сокетом, що станеться, коли програма припиняється».

Я не розумію, чому це можливо лише через додаток, який її відкриває ... Але я все одно хочу знати, чи є ще якийсь спосіб зробити це.


5
Ні, відкриті порти належать до того процесу, який їх відкрив, контроль неможливий ззовні. Що добре, або всі програми повинні передбачити, що їх відкриті порти (та файли) будуть заблоковані. Однак ви можете заблокувати трафік до порту через брандмауер (iptables), але це не закриє і відмовить порт для іншого використання.
Юрген Стробель

10
Дуже багато відповідей пропустили суть цього питання. Дурно заявляти, що лише програма, яка є власником порту, може відключити її. Я можу відключити його, підійшовши до коробки і витягнувши кабель Ethernet з розетки, або вбивши програму на іншому кінці з'єднання! Програма повинна бути написана для вирішення цього питання. Отже --- як ви перевірите, чи впевненість у тому, що програма написана правильно, не вимагаючи фізичного втручання та / або контролю іншого комп’ютера?
Дейл Вілсон

"... ніякого контролю неможливо ззовні." Це важливе зауваження, це мене керувало наступним питанням: як я можу бути частиною процесу ззовні? ГДБ.
Dankó Dávid

@ JürgenStrobel Насправді можливий контроль ззовні - і tcpkill, і ss можуть робити саме те, що вимагають. Оскільки відкриті порти справді не належать до процесу; вони є ресурсами ядра, з деякими правами, присвоєними процесу, але все ще існують лише на задоволення ядра.
Том Андерсон

@ tom-anderson DaleW: tcpkill - це інструмент брандмауера, і я згадував цей варіант. Ви можете запобігти трафіку до порту, який відрізняється від закриття порту (socket).
Юрген Стробель

Відповіді:


134

У мене була така ж проблема, процес повинен залишатися живим, але розетка повинна закриватися. Закрити розетку в запущеному процесі не неможливо, але складно:

  1. Знайдіть процес:

    netstat -np
    

    Ви отримуєте source/destination ip:port portstate pid/processnameкарту

  2. знайдіть дескриптор файлу сокета в процесі

    lsof -np $pid
    

    Ви отримуєте список: ім'я процесу, pid, користувач, fileDescriptor, ... рядок з'єднання.

    Знайдіть відповідний номер файлаDescriptor для з'єднання. Це буде щось на кшталт "97u", що означає "97".

  3. Тепер підключіть процес:

    gdb -p $pid
    
  4. Тепер закрийте розетку:

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

    приклад:

    call close(97)
    

    Потім від'єднайте gdb:

    quit
    

    І розетка закрита.


1
sudo lsof -np $pidдає мені близько 200 рядків, і я розгублений, як знайти бажаний FD. У моєму випадку процес - це вкладка Chrome, і я намагаюся закрити відкриті веб-розетки ...
SET

2
Рядок, як правило, виглядає так: firefox 14812 szupervigyor 97u IPv4 32814564 0t0 TCP 192.168.2.4:40385->173.194.39.65:https (ВИСТАВЛЕНО) як: process_name pid user fd [open_for] протокол пристрою inode Protocol_data_toString
Dankó Dadó

2
* Рядок типово виглядає так: firefox 14812 szupervigyor 97u IPv4 32814564 0t0 TCP 192.168.2.4:40385->173.194.39.65:https (ВИСТАВЛЕНО) як: process_name pid user fd [open_for] протокол пристрою inode Protocol_data_toString Вам потрібно знати віддалене адресу та знайдіть на останньому кол. У моєму прикладі 97 - це FileDescriptor. Пошук є складним, якщо ви відкрили декілька з'єднань з цільовим хостом.
Dankó Dávid

7
це геніальне рішення
marcorossi

8
Якщо ви хочете , щоб імітувати сокет закривається на віддаленому кінці (наприклад, рівний вихід) , то краще використовувати shutdown: call shutdown($fileDescriptor, 0).
екатмур

75

Ви ніби не задаєте тут неправильне запитання. Насправді неможливо просто «закрити порт» поза додатком, який відкрив сокет, слухаючи його. Єдиний спосіб зробити це - повністю знищити процес, яким належить порт. Потім, приблизно через хвилину-дві, порт знову стане доступним для використання. Ось що відбувається (якщо вам все одно, пропустіть до кінця, де я покажу вам, як вбити процес, що володіє певним портом):

Порти - це ресурси, виділені ОС на різні процеси. Це схоже на запит ОС для вказівника на файл. Однак, на відміну від покажчиків файлів, порт може мати власне лише ОДИН процес за один раз. Через інтерфейс сокетів BSD процеси можуть подати запит на прослуховування через порт, який ОС надасть. ОС також переконається, що жоден інший процес не отримує той же порт. У будь-який момент процес може звільнити порт, закривши сокет. Потім ОС поверне порт. Крім того, якщо процес закінчиться, не звільняючи порт, ОС з часом поверне порт (хоча це станеться не відразу: це займе кілька хвилин).

Тепер те, що ви хочете зробити (просто закрити порт з командного рядка), неможливо з двох причин. По-перше, якби це було можливо, це означало б, що один процес може просто вкрасти ресурс іншого процесу (порт). Це було б поганою політикою, якщо не обмежуватись лише пільговими процесами. Друга причина - незрозуміло, що буде з процесом, який володів портом, якщо ми дозволимо йому продовжувати працювати. Код процесу пишеться, припускаючи, що він володіє цим ресурсом. Якщо ми його просто забрали, це в кінцевому підсумку вийде з ладу, тому ОС не дозволяє вам це робити, навіть якщо ви привілейований процес. Натомість ви повинні їх просто вбити.

У будь-якому випадку, ось як вбити процес, який належить певному порту:

sudo netstat -ap | grep :<port_number>

Це виведе рядок, відповідний порту, що тримає процес, наприклад:

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

У цьому випадку procHoldingPort - це ім'я процесу, який відкрив порт, 4683 - його pid, а 8000 (зауважте, що це TCP) - номер порту, який він містить.

Потім, подивіться в останню колонку, ви побачите /. Потім виконайте це:

kill  <pid>

Якщо це не працює (ви можете перевірити, повторно запустивши команду netstat). Зробити це:

kill -9 <pid>

Загалом, краще уникати надсилання SIGKILL, якщо можете. Ось чому я кажу вам спробувати killраніше kill -9. Просто за допомогою killвідправляє ніжніший SIGTERM.

Як я вже говорив, для порту все одно знадобиться кілька хвилин, якщо ви це зробите. Я не знаю способу прискорити це. Якщо це хтось інший, я хотів би це почути.


@smehmood - Дякую за детальне пояснення .. Невеликий сумнів .. Як ядро ​​повертає відкритий порт різко вбитим процесом ?? .. рішення, що надається, здається, вбиває процес утримування порту ...

3
@codingfreak Ядро знає, що процес пройшов. Він знає, що може повернути порт. Насправді є правила щодо термінів закриття порту, щоб переконатися, що в мережі немає плавних пакетів. Звідки воно знає, що має ці ресурси? ось що робить ядро, відслідковує речі.
Багата Гомолка

Поки хтось не опублікує щось розумне, як unix.tools.port.close(<my port number>)я б використав init 6.
Snowcrash

Це відмінна відповідь на інше питання. Це питання стосується приєднання налагоджувача та зміни запущеної програми, щоб програма викликала функцію в дескрипторі файлу.
Артур Ульфельтт

19

Також можна використовувати фюзер

fuser -k -n *protocol portno*

Тут протокол - tcp / udp, а portno - номер, який ви хочете закрити. Напр

fuser -k -n tcp 37

Більше інформації на сторінці людини fuser


Просто вбивство володіння процесом для мене не спрацювало, але термінець зробив. Дякую!
webwurst

Я отримав змішані результати з ним, навіть після використання фюзера, я отримав "socket вже використовується", коли намагався повторно використовувати порт від убитого додатка, навіть роблячи це як root. З іншого боку, час звільнення розетки здавалося коротшим, ніж раніше, тому все одно дякую.
Ян Вльчинський

@JanVlcinsky Можливо, є "опікун" процес, який перезапускає вбитий момент через те fuser, що запущений?
RedBaron

@RedBaron: згідно з коментарем superuser.com/a/415236/153413 моя проблема виникає в погано написаному додатку, який не очищає / закриває сокет під час припинення. Так fuserзнаходить процес за допомогою порту і вбиває його, але не вирішує факту, що сокет не закритий. 60 секунд і ядро ​​робить це за мене.
Ян Вльчинський

6

Ви також можете використовувати iptables:

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

Це в основному здійснює те, що ви хочете. Це приведе весь трафік TCP до порту 80.


4
Ні, це дозволить зберегти розетку до тих пір, поки всі тайм-аути їх не закриють. (Він приховає весь трафік від процесу володіння, який потім не має можливості знати, він повинен закрити його.) Ви можете використовувати -j REJECTдля повернення прапор скидання TCP, який буде видно моїм процесом володіння (але лише тоді, коли намагається інша сторона щось надіслати).
Marki555

3
netstat -anp | grep 80

Він повинен вам сказати, якщо ви використовуєте apache, "httpd" (це лише приклад, використовуйте порт, який використовує ваша програма замість 80)

pkill -9 httpd 

або

killall -9 httpd

4
спробуйте нормальний вбити , перш ніж вдаватися до -9
Тіло

@omfgroflmao - Але це вб'є процес, який відкрив порт ??

@codingfreak Процес, який тримає порт, так, це вб'є його.

2

Можливо, ви могли просто дізнатися, з яким процесом відкрився сокет, з яким асоціюється порт, і вбити цей процес.

Але вам доведеться усвідомити, що якщо в цьому процесі немає обробника, який деаніціалізує всі речі, які він використовував (відкриті файли, розетки, вилки, речі, які можуть затримуватися, якщо вони не закриті належним чином після закінчення), ви створили б це затягуйте продуктивність системи. Плюс до цього, сокет залишатиметься відкритим, поки ядро ​​не зрозуміє, що процес був убитий. Зазвичай це займає близько хвилини.

Я припускаю, що краще питання було б: на якому порту (що належить до того процесу) ви хочете зупинитись?

Якщо ви намагаєтесь покінчити із знайденим заднім краєм або вірусом, то вам слід принаймні дізнатися, які дані рухаються вперед і назад, перш ніж припинити їх. (wireshark хороший для цього) (І виконуване ім'я процесу, щоб ви могли його видалити і не допустити його повернення при перезавантаженні) або, якщо це щось встановлене (наприклад, HTTPD або FTPD або щось подібне), тоді ви вже повинні мати доступ до сам процес.

Зазвичай у нього буде програма управління (зупинка HTTPD | запуск | або щось таке). Або, якщо це системна річ, ви, мабуть, не повинні з цим возитися. У всякому разі, я думав, що оскільки всі інші надають тобі кут "як", я повинен дати тобі застереження.


Дуже хороший коментар. У мене є одна програма, яка після закінчення закриває сокет, що призводить до описаної поведінки - сокет є непридатним протягом приблизно 60 секунд. Коли я зупиняюсь і запускаю цей процес, я протягом хвилини скаржиться, що адреса та порт уже використовуються. Найкраще рішення - виправити погано поводиться процес, щоб закрити належним чином, але іноді це не варіант. Чи є спосіб попросити ядро ​​перевірити заблокований сокет раніше, ніж за 60 секунд?
Ян Вльчинський

2

Спочатку я шукав процеси монго та вузла, а потім робив наступне:

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

Після ідентифікації просто вбийте процеси за допомогою команди kill.

kill -9 10418
kill -9 10490

Нарешті, введіть meteorі він повинен працювати знову.


1

Ви можете написати сценарій, який змінив iptables і перезапустив їх. Один сценарій для додавання правила, що скидає всі пакети на порт, інший сценарій для видалення цього правила.

Інші відповіді показали вам, як вбити процес, прив’язаний до порту - це може бути не тим, що ви хочете. Якщо ви хочете, щоб сервер продовжував працювати, але для запобігання з'єднань з клієнтами, ви хочете заблокувати порт, а не зупиняти процес.


@Michael Shimmins ... Хм звучить цікаво, оскільки ми можемо заблокувати порт на стороні сервера, щоб клієнт не міг надсилати будь-які повідомлення.

Добре, що клієнт може надсилати повідомлення все, що хоче, ми щойно закрили двері, щоб вони не змогли зайти.

1

Ще одне питання: колись ядро ​​володіє самими портами. Я знаю, що маршрутизація NAT містить деякі порти, відкриті для використання NAT. Ви не можете вбити процес для цього, це ядро, потрібна переконфігурація та перезавантаження.


1

Якщо ви хочете, щоб ваш порт вийшов швидше, вам слід встановити наступне значення:

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

встановити його від 60 секунд (за замовчуванням) до 1 секунди


1

Ви можете використовувати команду з назвою killcx, закриваючи з'єднання без жодного процесу для вбивства.

  • синтаксис:
killcx [dest_ip:dest_port] {interface}

  dest_ip              : remote IP
  dest_port            : remote port
  interface (optional) : network interface (eth0, lo etc).
  • приклад:
killcx 120.121.122.123:1234
killcx 120.121.122.123:1234 eth0

// Чи мають більшість Linux-файлів killcx? Жоден із тих, кого я намагався, не мав цієї команди killcx доступною без встановлення недоступних для них пакетів із поточним конфігурацією сховища.
Натан Басанес

ні, ви можете знайти інструмент тут: killcx.sourceforge.net
shuaiming

//, я знаю, що я можу знайти інструмент, це просто біль встановити це на тисячах серверів.
Натан Басанес

Використовуйте лялечку @NathanBasanese :)
SHOUBHIK BOSE

1

Я знаю, що ця відповідь не відповідає саме на це питання, строго кажучи, але, читаючи в ньому, це може бути відповідною інформацією:

Поведінка за замовчуванням прив'язки сокета до порту (та адреси) полягає в тому, що коли сокет закритий різким припиненням процесу, сокет деякий час залишатиметься в TIME_WAIT. Це означає, що ви не можете негайно повторно зв’язати цю адресу / порт. Якщо ви розробляєте саму систему через стандартний інтерфейс сокетів BSD, ви можете (принаймні певною мірою) контролювати цю поведінку за допомогою параметра SO_REUSEADDR socket. Це в основному дозволить вам знову прив'язатись до тієї ж адреси / порту, якщо сокет знаходиться у статусі TIME_WAIT. Все ж одна розетка на порт!

Однак ця інформація повинна використовуватися лише як допомога розвитку, оскільки існує причина, що TIME_WAIT існує в першу чергу, що вже було пояснено в інших відповідях.


//, Право. Звичайно, процеси вбивства - не найкращий спосіб звільнення портів. Я помітив, що коли я вбиваю процес Вагранта, якщо він висить, я не можу знову бродять. Б'юсь об заклад, що ця штука TIME_WAIT має щось спільне з цим.
Натан Басанес

0

Якщо ви не хочете, щоб через сокет не було трафіку, але ви хочете зберегти процес: tcpkill .

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

Почнеться нюхаючий демон, який перехоплює і руйнує весь трафік на цьому порту. Дуже зручно для тестування ваших програм на мережеві розбиття.


0

Ви можете закрити прослуховувальну розетку за допомогою ss :

sudo ss --kill state listening src :1234

Де 1234 - номер вашого порту.

ss є частиною пакету iproute2, тому є сильна зміна, вона вже встановлена ​​на сучасному Linux.

Я дізнався про це з відповіді на пов’язане питання .

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.