Вопрос или проблема
У меня есть bash-скрипт, который запускается при подключении и отключении адаптера питания на основе этого правила udev:
#/etc/udev/rules.d/powersave.rules
SUBSYSTEM=="power_supply", ATTR{online}=="0", RUN+="/path/to/script"
SUBSYSTEM=="power_supply", ATTR{online}=="1", RUN+="/path/to/script"
В этом скрипте я делаю POST-запрос с помощью curl для отправки вебхука Discord.
#!/bin/bash
echo "Running" >> ~/scripts/run.log
curl -H "Content-Type:application/json" -X POST "$discord_url" -d '{"content":"some_msg"}' >> ~/scripts/output.log 2>> ~/scripts/error.log
Этот скрипт работает нормально, когда я выполняю его напрямую с правами sudo или как обычный пользователь, но когда скрипт выполняется udev, я получаю следующую ошибку:
curl: (6) Could not resolve host: discord.com
Я пробовал запускать nslookup
из скрипта. И те же результаты, не работает при выполнении через udev
;; error связи с 127.0.0.53#53: превышено время ожидания
;; error связи с 127.0.0.53#53: превышено время ожидания
;; error связи с 127.0.0.53#53: превышено время ожидания
;; серверы недоступны
ОС: Ubuntu 22.04
Буду признателен за любую помощь. Спасибо.
Редактировать:
resolvectl status
вывод:
Глобально
Протоколы: -LLMNR -mDNS -DNSOverTLS DNSSEC=no/unsupported
Режим resolv.conf: stub
Интерфейс 2 (enp6s0f5)
Текущие области: нет
Протоколы: -DefaultRoute +LLMNR -mDNS -DNSOverTLS DNSSEC=no/unsupported
Интерфейс 3 (wlp5s0)
Текущие области: DNS
Протоколы: +DefaultRoute +LLMNR -mDNS -DNSOverTLS DNSSEC=no/unsupported
Текущий DNS сервер: 192.168.1.1
DNS сервера: 192.168.1.1 fe80::1%23007
Интерфейс 4 (docker0)
Текущие области: нет
Протоколы: -DefaultRoute +LLMNR -mDNS -DNSOverTLS DNSSEC=no/unsupported
Интерфейс 5 (br-994938332bdf)
Текущие области: нет
Протоколы: -DefaultRoute +LLMNR -mDNS -DNSOverTLS DNSSEC=no/unsupported
Интерфейс 591 (br-4ed8e6432cf7)
Текущие области: нет
Протоколы: -DefaultRoute +LLMNR -mDNS -DNSOverTLS DNSSEC=no/unsupported
Интерфейс 593 (veth2b8fb85)
Текущие области: нет
Протоколы: -DefaultRoute +LLMNR -mDNS -DNSOverTLS DNSSEC=no/unsupported
Интерфейс 673 (veth2cd0292)
Текущие области: нет
Протоколы: -DefaultRoute +LLMNR -mDNS -DNSOverTLS DNSSEC=no/unsupported
Я не думаю, что вы можете получить доступ к сети из процесса udevd
. Вы можете увидеть это в systemd-udevd.service
:
$ systemctl cat systemd-udevd.service | grep IPAddressDeny
IPAddressDeny=any
$ systemctl show -p IPAddressDeny systemd-udevd.service
IPAddressDeny=0.0.0.0/0 ::/0
что, по моему мнению, означает, что доступ к сети запрещен. См. man systemd.resource-control
для IPAddressDeny
.
Вы можете создать небольшой блок systemd для запуска скрипта, а затем
запустить его из правила udev. Например, создайте /etc/systemd/system/myudev.service
с содержанием:
[Unit]
Description=my udev script
[Service]
ExecStart=/path/to/script
и добавьте правила, такие как:
SUBSYSTEM=="power_supply", ATTR{online}=="0", TAG+="systemd", ENV{SYSTEMD_USER_WANTS}="myudev.service"
Ответ или решение
Если вы столкнулись с проблемой "curl: Could not resolve host" при выполнении через правило udev, это может быть связано с тем, что служба udev изолирована от сетевых ресурсов. Это ограничение происходит из-за настройки IPAddressDeny=any
, которая запрещает доступ ко всем сетевым интерфейсам из udev.
Проблема
Когда вы выполняете ваш скрипт через udev, система блокирует сетевые запросы. По умолчанию udev работает в сильно ограниченной среде, где доступ к сети запрещен, что и приводит к ошибке разрешения DNS.
Решение
Для решения этой проблемы, вам следует использовать службу systemd для выполнения скрипта с необходимыми сетевыми разрешениями. В этом случае, udev будет запускать службу systemd, а не напрямую выполнять скрипт. Это позволяет избежать ограничения доступа к сети.
Пошаговое руководство
-
Создание службы systemd
Создайте файл службы в
/etc/systemd/system/myudev.service
с содержимым:[Unit] Description=Скрипт для udev After=network-online.target [Service] ExecStart=/path/to/script
Данный файл описывает службу, которая будет запускать ваш скрипт. Обратите внимание на
After=network-online.target
, который гарантирует, что служба будет запущена только после установления сетевого соединения. -
Модификация правила udev
Измените ваше правило udev в файле
/etc/udev/rules.d/powersave.rules
на следующее:SUBSYSTEM=="power_supply", ATTR{online}=="0", TAG+="systemd", ENV{SYSTEMD_USER_WANTS}="myudev.service" SUBSYSTEM=="power_supply", ATTR{online}=="1", TAG+="systemd", ENV{SYSTEMD_USER_WANTS}="myudev.service"
Эти строки добавляют тег
systemd
и указывают udev вызывать вашу службу systemd при изменении состояния питания. -
Обновление udev и systemd конфигурации
После внесения изменений выполните следующие команды, чтобы обновить конфигурацию:
sudo udevadm control --reload-rules sudo systemctl daemon-reload
-
Тестирование
Теперь подключите и отключите адаптер питания, чтобы убедиться, что служба успешно запускается, и скрипт выполняется с разрешением сетевых адресов.
Заключение
Переход на использование systemd для выполнения сетевых задач из udev — это устойчивое решение, учитывающее ограничения безопасности, присущие среде udev. Настраивая отдельные службы для таких задач, вы сохраняете возможности управления системой и подключением к сети без упрощения безопасности.