多用户嵌入式系统中user1调用D-Bus systemd启停服务遇权限错误
问题描述
我在一个包含root、user1等多用户的嵌入式系统中,以user1身份运行C++二进制文件时,调用systemd启停服务接口会触发权限错误,以root身份运行则正常。
相关代码、测试程序、service文件及错误输出如下:
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); } }
service文件(路径:/etc/systemd/system/foo-daemon.service)
[Unit] Description=Foo [Service] ExecStart=/usr/local/bin/foo-daemon [Install] WantedBy=multi-user.target
user1运行时错误输出
Exception in SDCallMethodSS(): Could not send StopUnit command to systemd for service: foo-daemon.service. Error: Permission denied
解决方案
以下几种方法可解决user1的权限问题,按需选择:
方法1:添加PolicyKit规则精准授权
创建PolicyKit规则文件/etc/polkit-1/rules.d/50-foo-daemon.rules,内容如下:
polkit.addRule(function(action, subject) { if (action.id == "org.freedesktop.systemd1.manage-units" && action.lookup("unit") == "foo-daemon.service" && subject.user == "user1") { return polkit.Result.YES; } });
该规则仅允许user1对foo-daemon.service执行启停等unit管理操作,权限范围精准,无需重启服务即可生效。
方法2:给二进制文件添加系统管理能力
若系统支持Linux Capabilities,可给调用程序添加CAP_SYS_ADMIN权限:
sudo setcap cap_sys_admin+ep /path/to/your/cpp_binary
此方法让user1运行该二进制时拥有systemd管理所需权限,但权限范围较宽,仅在必要时使用。
方法3:让服务以user1身份运行
修改foo-daemon.service文件,让服务本身以user1身份启动,此时user1默认拥有管理该服务的权限:
- 更新service文件内容:
[Unit] Description=Foo [Service] ExecStart=/usr/local/bin/foo-daemon User=user1 [Install] WantedBy=multi-user.target
- 重新加载systemd配置并重启服务:
sudo systemctl daemon-reload sudo systemctl restart foo-daemon.service
此方法适合服务本身不需要root权限的场景。
内容的提问来源于stack exchange,提问作者preetam
相关产品推荐
相关产品推荐

