Вопрос или проблема
Я попробовал как 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
, а также написание скриптов для автоматизации процесса. Надеюсь, это поможет вам в вашем проекте!