Как я могу отобразить все подключения к моему хосту, включая подключения к LXC-гостям?

Вопрос или проблема

Я попробовал как netstat, так и lsof, но, похоже, нет возможности увидеть соединения с моими LXC гостями.

Существует ли способ достичь этого … для всех гостей сразу?


То, что меня здесь смущает, – это тот факт, что я могу видеть процессы гостей, если запускаю от имени суперпользователя. Я также вижу veth интерфейсы, которые динамически создаются для каждого гостя. Почему я не могу увидеть соединения процессов, которые иначе видны?

Ядро указывает состояние соединений в /proc/net/tcp, /proc/net/udp и т.д., но поскольку пространства имен разделяют сетевой стек, если приложение работает внутри контейнера (в другом пространстве пользователей) и подключено к сети, хост /proc/net/tcp не покажет его соединение.

conntrack можно использовать для отображения соединений всей машины но это не работает для некоторых интерфейсов, таких как wireguard…

ip -all netns exec command можно использовать для выполнения команд внутри всех пространств пользователей но это ограничено пространствами пользователей, созданными с помощью команды ip.

С точки зрения приложения, работающего в контейнере, его состояние сетевого стека все еще видимо на хосте по адресу /proc/$pid/net/tcp, поэтому в качестве обходного пути, ожидая написания инструмента на C, я написал небольшой bash-скрипт, который проходит по /proc/$pid/net/tcp[udp] и объединяет все состояния для того, чтобы можно было перечислить соединения всей машины.

Скрипт сначала объединяет все /proc/$pid/net/tcp или /proc/$pid/net/udp, сортирует их, удаляет дубликаты, преобразует значения в читаемый текст и выводит их (скрипт требует find, grep, xargs, awk, strtonum, sort и uniq).

Для TCP

find /proc/ 2>/dev/null | grep tcp | grep -v task | grep -v sys/net | xargs grep -v rem_address 2>/dev/null | awk '{x=strtonum("0x"substr($3,index($3,":")-2,2)); y=strtonum("0x"substr($4,index($4,":")-2,2)); for (i=5; i>0; i-=2) x = x"."strtonum("0x"substr($3,i,2)); for (i=5; i>0; i-=2) y = y"."strtonum("0x"substr($4,i,2))}{printf ("%s\t:%s\t ----> \t %s\t:%s\t%s\n",x,strtonum("0x"substr($3,index($3,":")+1,4)),y,strtonum("0x"substr($4,index($4,":")+1,4)),$1)}' | sort | uniq --check-chars=25

Для UDP

find /proc/ 2>/dev/null | grep udp | grep -v task | grep -v sys/net | xargs grep -v rem_address 2>/dev/null | awk '{x=strtonum("0x"substr($3,index($3,":")-2,2)); y=strtonum("0x"substr($4,index($4,":")-2,2)); for (i=5; i>0; i-=2) x = x"."strtonum("0x"substr($3,i,2)); for (i=5; i>0; i-=2) y = y"."strtonum("0x"substr($4,i,2))}{printf ("%s\t:%s\t ----> \t %s\t:%s\t%s\n",x,strtonum("0x"substr($3,index($3,":")+1,4)),y,strtonum("0x"substr($4,index($4,":")+1,4)),$1)}' | sort | uniq --check-chars=25

Вывод выглядит так: (обратите внимание, что pid не точен и используется только для идентификации контейнера)

127.0.0.1      :80       ---->   0.0.0.0        :0      /proc/10176/net/tcp:
192.168.0.2    :33882    ---->   192.30.253.125 :443    /proc/10176/net/tcp
192.168.0.2    :34020    ---->   192.30.253.125 :443    /proc/10176/net/tcp:
192.168.0.2    :34162    ---->   192.30.253.125 :443    /proc/10176/net/tcp:
192.168.0.2    :36242    ---->   192.30.253.124 :443    /proc/10176/net/tcp:
192.168.0.2    :37324    ---->   192.30.253.124 :443    /proc/10176/net/tcp:
192.168.0.2    :40122    ---->   216.239.38.21  :80     /proc/10176/net/tcp:
192.168.0.2    :40124    ---->   216.239.38.21  :80     /proc/10176/net/tcp:

Также я нашел отличный инструмент для управления пространствами имен с некоторыми очень полезными командами, который называется nsutils.

Я думаю, что https://stackoverflow.com/a/40352004/1951468 должен ответить на ваш вопрос.

Так что, в основном, единственный способ – это цикл вокруг ваших контейнеров и nsenter.

Я думаю, вы можете использовать sudo conntrack -L.

Conntrack – это программа командной строки в пространстве пользователей, предназначенная для системных администраторов. Она позволяет им просматривать и управлять таблицей состояния отслеживания соединений в ядре.

Если не установлено, необходимый пакет – это conntrack-tools (на Fedora и т.д.) или conntrack (на Debian, Ubuntu и т.д.).

Похожий обходной путь – это выполнить команду netstat/lsof во всех сетевых пространствах имен с помощью

sudo ip -all netns exec ss -p -ut 

или

sudo ip -all netns exec lsof -i

или

sudo ip -all netns exec netstat -ltup 

Вы можете перечислить все (sudo lsns) сетевые пространства имен (-t net) пути (-o PATH) без заголовка (-n) и выполнить цикл, чтобы войти во все пространства имен (sudo nsenter -n <path>), чтобы перечислить все TCP соединения в них (ss -t).

sudo lsns -t net -no PATH | while read ns; do sudo nsenter -n$ns ss -t; done

Мне больше нравится новый ss из iproute2, чем старый netstat.

Пока что мне повезло получить соединения контейнера, запуская netstat из контейнера:

sudo docker exec -it <containerIdOrName> netstat

Очевидно, контейнер должен иметь его установленным.

Ответ или решение

Для отображения всех соединений к вашему хосту, включая соединения к LXC-гостям, существует несколько подходов. Понимание различий в сетевых пространствах имен (namespaces) и динамических виртуальных интерфейсах (veth), используемых контейнерами, является ключевым моментом в этой задаче.

1. Понимание пространств имен

Каждый контейнер создает свое собственное сетевое пространство имен, и, следовательно, соединения, устанавливаемые в контейнерах, не отображаются в глобальной таблице соединений хоста (например, в /proc/net/tcp). Это происходит потому, что каждое пространство имен имеет свой собственный стек сетевых протоколов.

2. Использование conntrack

Как вы уже упомянули, conntrack может быть полезен для отображения всех соединений, но имейте в виду, что он может не работать для некоторых интерфейсов, таких как WireGuard. Убедитесь, что утилита установлена:

sudo apt install conntrack

А затем выполните команду:

sudo conntrack -L

3. Просмотр соединений в пространстве имен

Чтобы просмотреть соединения в каждом пространстве имен, вы можете воспользоваться командами ip и nsenter. Вот несколько способов сделать это:

Использование ip и ss

Одним из самых эффективных способов является выполнение команды ss в каждом сетевом пространстве имен. Следующий код проходит по всем пространствам имен и отображает текущие TCP соединения:

sudo ip netns list | awk '{print $1}' | while read ns; do
    echo "Namespace: $ns"
    sudo ip netns exec $ns ss -t -p
done

Эта команда выведет все соединения для каждого пространства имен.

Скрипт для выполнения ss и lsof

Кроме того, вы можете использовать скрипт для выполнения команд, таких как lsof или netstat, в каждом пространстве имен:

sudo ip -all netns exec ss -p -ut 

Или:

sudo ip -all netns exec lsof -i

4. Использование find для обработки /proc

Если вы хотите получить данные напрямую из /proc, вы можете использовать следующий подход на bash. Этот скрипт будет проходить по всем процессам и выводить соединения TCP и UDP:

# Для TCP
find /proc/ 2>/dev/null | grep tcp | grep -v task | grep -v sys/net | xargs grep -v rem_address 2>/dev/null | awk '{
    #обработка вывода
}' | sort | uniq --check-chars=25

# Для UDP
find /proc/ 2>/dev/null | grep udp | grep -v task | grep -v sys/net | xargs grep -v rem_address 2>/dev/null | awk '{
    #обработка вывода
}' | sort | uniq --check-chars=25

Этот скрипт попытается объединить данные соединений из всех процессов, работающих на хосте и контейнерах. Однако могут возникнуть ограничения из-за прав доступа.

5. Использование durable-tools

Рекомендуется обратить внимание на инструмент nsutils, который предоставляет более удобные команды для управления пространствами имен. Он может упростить процесс доступа к соединениям в контейнерах и предоставит дополнительные функции для администрирования.

Заключение

Суммируя вышесказанное, для полного отображения соединений, включая те, что происходят в контейнерах, вам нужно будет использовать комбинацию команд и утилит для работы с пространствами имен. Это может быть достигнуто через conntrack, ip, ss, lsof, а также написание скриптов для автоматизации процесса. Надеюсь, это поможет вам в вашем проекте!

Оцените материал
Добавить комментарий

Капча загружается...