내 프로그램이 d-bus를 사용하여 systemd에 액세스하여 서비스를 시작 및 중지하는 이유는 다중 사용자 임베디드 시스템에서 권한 오류가 발생하는 이유입니다.

내 프로그램이 d-bus를 사용하여 systemd에 액세스하여 서비스를 시작 및 중지하는 이유는 다중 사용자 임베디드 시스템에서 권한 오류가 발생하는 이유입니다.

제가 작업 중인 임베디드 시스템에는 "root" 및 "user1"과 같은 여러 사용자가 있습니다. "user1"로 로그인한 C++ 바이너리를 실행 중인데 권한 오류로 인해 서비스를 시작/중지하지 못합니다. 동일한 바이너리는 루트에서 실행될 때 잘 작동합니다. 코드는 다음과 같습니다.

#include <iostream>
#include <systemd/sd-bus.h>


static void SDCallMethodSS(
  sd_bus* bus,
  const std::string& name,
  const std::string& method)
{
  sd_bus_error err = SD_BUS_ERROR_NULL;
  sd_bus_message* msg = nullptr;
  int r;

  r = sd_bus_call_method(bus,
      "org.freedesktop.systemd1",
      "/org/freedesktop/systemd1",
      "org.freedesktop.systemd1.Manager",
      method.c_str(),
      &err,
      &msg,
      "ss",
      name.c_str(),  "replace" );

  if (r < 0)
  {
    std::string err_str("Could not send " + method +
                        " command to systemd for service: " + name +
                        ". Error: " + err.message );

    sd_bus_error_free(&err);
    sd_bus_message_unref(msg);
    throw std::runtime_error(err_str);
  }

  char* response;
  r = sd_bus_message_read(msg, "o", &response);
  if (r < 0)
  {
          std::cerr<< "Failed to parse response message: " << strerror(-r) << std::endl;;
  }

  sd_bus_error_free(&err);
  sd_bus_message_unref(msg);
}

int main() {
  int r;
  sd_bus *bus = NULL;

  r = sd_bus_open_system(&bus);
  if (r < 0) {
          std::cerr<< "Failed to connect to system bus: " << strerror(-r) << std::endl;
    return -1;
  }

  try{
    SDCallMethodSS(bus, std::string("foo-daemon.service"), std::string("StopUnit"));
  } catch (std::exception& e) {
    std::cout << "Exception in SDCallMethodSS(): " << e.what() << std::endl;
    return -2;
  }
}

Foo-daemon은 가상 프로그램입니다.

#include <unistd.h>

int main()
{
  while(1){
    sleep(1);
  }

}

서비스 파일은 간단합니다.

[Unit]
Description=Foo

[Service]
ExecStart=/usr/local/bin/foo-daemon

[Install]
WantedBy=multi-user.target

서비스 파일을 /etc/systemd/system 'user1'에 로드하면 출력은 다음과 같습니다.

Exception in SDCallMethodSS(): Could not send StopUnit command to systemd for service: foo-daemon.service. Error: Permission denied

"user1"의 권한 문제를 해결하는 방법

답변1

문제는 여기서 시작됩니다:

r = sd_bus_open_system(&bus);

그러면 시스템의 버스가 열립니다. 그러면 실행할 때와 동일한 동작이 발생합니다.

user1@machine:~$ systemctl ...

sd-bus API를 사용하든 systemctl을 사용하든 systemd동일한 방식으로 인증됩니다. user1장치를 시작/중지할 권한이 없습니다.


대안 1: --사용자 버스

대안은 다음을 사용하는 것입니다.

r = sd_bus_open_user(&bus);

이는 using과 유사 systemctl --user ...하지만 프로세스는 using과 동일한 권한을 가지며 버스에서만 실행 user1됩니다 .user1


대안 2: polkit 규칙(사용자 권한)

시스템 버스에서 장치를 시작/중지할 systemd수 있도록 구성해야 합니다 . user1이를 통해polkit

Debian 기반 시스템(polkit < 106)을 사용하는 경우 다음 파일을 생성하여 규칙을 생성합니다 *.pkla.

/etc/polkit-1/localauthority/50-local.d/service-auth.pkla
---
[Allow user1 to start/stop/restart services]
Identity=unix-user:user1
Action=org.freedesktop.systemd1.manage-units
ResultActive=yes

대안 3: polkit 규칙(서비스별 권한)

Redhat/Arch 기반 시스템(polkit >=106)을 사용하는 경우 더 구체적으로 지정할 수 있는 javascript 유형 구문이 있습니다. 이 경우 모든 사용자가 foo-daemon.service파일을 관리하도록 허용할 수 있습니다 *.rules.

/etc/polkit-1/rules.d/foo-daemon.rules
---
polkit.addRule(function(action, subject) {
    if (action.id == "org.freedesktop.systemd1.manage-units") {
        if (action.lookup("unit") == "foo-daemon.service") {
            var verb = action.lookup("verb");
            if (verb == "start" || verb == "stop" || verb == "restart") {
                return polkit.Result.YES;
            }
        }
    }
});

대안 4: polkit 규칙(그룹 권한)

제가 사용하고 싶은 솔루션은 그룹 구성원에게 단위를 스냅할 수 있는 권한을 부여하는 것입니다. 그러면 사용자가 해당 그룹의 구성원인 한 다음을 수행할 수 있습니다 systemctl {start,stop,restart} ....sd_bus_open_system(...)

이를 수행하는 방법에 대한 답변은 다음과 같습니다.

systemd는 그룹에서 권한이 없는 사용자로 시작됩니다.

관련 정보