curl: Не удалось разрешить имя хоста при выполнении через правило udev

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

У меня есть 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, а не напрямую выполнять скрипт. Это позволяет избежать ограничения доступа к сети.

Пошаговое руководство

  1. Создание службы systemd

    Создайте файл службы в /etc/systemd/system/myudev.service с содержимым:

    [Unit]
    Description=Скрипт для udev
    After=network-online.target
    
    [Service]
    ExecStart=/path/to/script

    Данный файл описывает службу, которая будет запускать ваш скрипт. Обратите внимание на After=network-online.target, который гарантирует, что служба будет запущена только после установления сетевого соединения.

  2. Модификация правила 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 при изменении состояния питания.

  3. Обновление udev и systemd конфигурации

    После внесения изменений выполните следующие команды, чтобы обновить конфигурацию:

    sudo udevadm control --reload-rules
    sudo systemctl daemon-reload
  4. Тестирование

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

Заключение

Переход на использование systemd для выполнения сетевых задач из udev — это устойчивое решение, учитывающее ограничения безопасности, присущие среде udev. Настраивая отдельные службы для таких задач, вы сохраняете возможности управления системой и подключением к сети без упрощения безопасности.

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

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