如何在systemd服务中设置环境变量?

lfa*_*des 275 systemd

我有一个带有 systemdArch Linux 系统,我已经创建了自己的服务。配置服务/etc/systemd/system/myservice.service如下所示:

[Unit]
Description=My Daemon

[Service]
ExecStart=/bin/myforegroundcmd

[Install]
WantedBy=multi-user.target
Run Code Online (Sandbox Code Playgroud)

现在我想为/bin/myforegroundcmd. 我怎么做?

Mic*_*ton 347

时代在变,最佳实践也在变。

当前要做到这一点最好的方法是运行systemctl edit myservice,它会为你创建一个覆盖文件或让您编辑现有的一个。

在正常安装中,这将创建一个目录/etc/systemd/system/myservice.service.d,并在该目录中创建一个名称以.conf(通常为override.conf)结尾的文件,在此文件中,您可以添加或覆盖发行版提供的单元的任何部分。

例如,在一个文件中/etc/systemd/system/myservice.service.d/myenv.conf

[Service]
Environment="SECRET=pGNqduRFkB4K9C2vijOmUDa2kPtUhArN"
Environment="ANOTHER_SECRET=JP8YLOc2bsNlrGuD6LVTq7L36obpjzxd"
Run Code Online (Sandbox Code Playgroud)

另请注意,如果目录存在且为空,您的服务将被禁用!如果您不打算将某些内容放入目录中,请确保它不存在。


作为参考,旧方法是:

推荐的方法是创建一个/etc/sysconfig/myservice包含变量的文件,然后使用EnvironmentFile.

有关完整的详细信息,请参阅 Fedora 关于如何编写 systemd 脚本的文档。

  • 不不不。不推荐使用 /etc/sysconfig。不鼓励使用 debian 中的 /etc/default/*,因为它们毫无意义,而且名称毫无意义,仅出于向后兼容的原因才有意义(所有 /etc 都与系统配置有关,而不仅仅是 /etc/ sysconfig 和 /etc/defaults 用于覆盖,而不是默认值)。只需将定义直接放在单元文件中,或者如果不可能,则放在具有包特定位置的环境文件中(如 Michał 的评论建议)。 (5认同)
  • 我猜 `sysconfig` 路径特定于 Fedora,但问题是关于 Arch Linux。paluh 的回答我觉得更有趣 (4认同)
  • @MichaelHampton 您能否为“当前最佳方式”添加文档链接? (3认同)
  • 不要使用 `Environment=` 来传递密码之类的秘密。有关详细信息,请参阅 [我的回答](https://serverfault.com/a/910655/1143)。 (2认同)

小智 102

答案取决于变量是应该是常量(即不应该由用户获取单位来修改)还是变量(应该由用户设置)。

由于它是您的本地单位,因此边界非常模糊,无论哪种方式都可以。但是,如果您开始分发它并最终以/usr/lib/systemd/system.

常数值

如果每个实例的值不需要更改,首选方法是将其作为Environment=, 直接放在单元文件中:

[Unit]
Description=My Daemon

[Service]
Environment="FOO=bar baz"
ExecStart=/bin/myforegroundcmd

[Install]
WantedBy=multi-user.target
Run Code Online (Sandbox Code Playgroud)

这样做的好处是变量与单元保存在一个文件中。因此,单元文件更容易在系统之间移动。

变量值

但是,当 sysadmin 应该在本地更改环境变量的值时,上述解决方案不起作用。更具体地说,每次更新单元文件时都需要设置新值。

对于这种情况,将使用一个额外的文件。如何 - 通常取决于分销政策。

一种特别有趣的解决方案是使用/etc/systemd/system/myservice.service.d目录。与其他解决方案不同,该目录由 systemd 本身支持,因此没有特定于发行版的路径。

在这种情况下,您放置一个这样的文件/etc/systemd/system/myservice.service.d/local.conf,添加单元文件的缺失部分:

[Service]
Environment="FOO=bar baz"
Run Code Online (Sandbox Code Playgroud)

之后,systemd 会在启动服务时合并这两个文件(请记住systemctl daemon-reload更改其中任何一个后)。并且由于该路径由 systemd 直接使用EnvironmentFile=,因此您不使用此路径。

如果应该仅在某些受影响的系统上更改该值,您可以组合两种解决方案,直接在单元中提供默认值,并在另一个文件中提供本地覆盖。

  • `EnvironmentFile=` 当值是密码之类的秘密时会更好。有关详细信息,请参阅 [我的回答](https://serverfault.com/a/910655/1143)。 (2认同)

pal*_*luh 50

http://0pointer.de/public/systemd-man/systemd.exec.html#Environment= - 你有两个选择(迈克尔已经指出了一个):

Environment=
Run Code Online (Sandbox Code Playgroud)

EnvironmentFile=
Run Code Online (Sandbox Code Playgroud)

  • 只需将其留在这里:如果您决定使用 EnviromentFile=,请确保您的文件不包含“export VARIABLE=VALUE”语句,而仅包含“VARIABLE=VALUE”基本语句以使其正常工作。认为这可能会帮助某人。 (12认同)

Don*_*kby 34

迈克尔米查的答案很有帮助并回答了如何为 systemd 服务设置环境变量的原始问题。但是,环境变量的一个常见用途是将敏感数据(如密码)配置在一个不会意外地将应用程序代码提交到源代码控制的位置。

如果这就是为什么你要一个环境变量传递给你的服务,使用Environment=的单元配置文件中 使用EnvironmentFile=并将其指向另一个只能由服务帐户(以及具有 root 访问权限的用户)读取的配置文件。

使用此命令的任何用户都可以看到单元配置文件的详细信息:

systemctl show my_service
Run Code Online (Sandbox Code Playgroud)

我把一个配置文件/etc/my_service/my_service.conf放在那里,把我的秘密放在那里:

MY_SECRET=correcthorsebatterystaple
Run Code Online (Sandbox Code Playgroud)

然后在我的服务单元文件中,我使用了EnvironmentFile=

[Unit]
Description=my_service

[Service]
ExecStart=/usr/bin/python /path/to/my_service.py
EnvironmentFile=/etc/my_service/my_service.conf
User=myservice

[Install]
WantedBy=multi-user.target
Run Code Online (Sandbox Code Playgroud)

我检查了ps auxe看不到那些环境变量,其他用户无权访问/proc/*/environ. 当然,请检查您自己的系统。

  • 正如我所说,“systemctl show my_service”将向任何用户显示单元配置文件内容,包括“Environment=”。试试看。 (7认同)

小智 11

Michael 提供了一个干净的解决方案,但我想从脚本中获取更新的 env 变量。不幸的是,在 systemd 单元文件中无法执行 bash 命令。幸运的是,您可以在 ExecStart 中触发 bash:

http://www.dsm.fordham.edu/cgi-bin/man-cgi.pl?topic=systemd.service&sect=5

请注意,此设置不直接支持 shell 命令行。如果要使用 shell 命令行,则需要将它们显式传递给某种类型的 shell 实现。

我们的例子是这样的:

[Service]
ExecStart=/bin/bash -c "ENV=`script`; /bin/myforegroundcmd"
Run Code Online (Sandbox Code Playgroud)

  • 由于多种原因,这将不起作用(除非它是“一次性”服务,这是毫无意义的)。我设法使以下内容起作用:`/bin/bash -a -c 'source /etc/sysconfig/whatever && execwhatever-program'`。`-a` 确保环境被导出到子进程(除非你想在 `whatever` 中为所有变量加上 `export` 前缀) (8认同)
  • 有一种方法可以在 systemd 服务文件“中”执行 bash 命令。请参阅此链接:https://coreos.com/os/docs/latest/using-environment-variables-in-systemd-units.html (2认同)

小智 10

不要使用Environment= 或EnvironmentFile= 作为凭据/机密。

\n

根据https://www.freedesktop.org/software/systemd/man/systemd.exec.html#Environment

\n

你应该使用LoadCredential=, LoadCredentialEncrypted= or SetCredentialEncrypted=

\n
\n

请注意,环境变量不适合将机密(例如密码、密钥材料、\xe2\x80\xa6)传递给服务进程。为单元设置的环境变量通过 D-Bus IPC 暴露给非特权客户端,并且通常不被理解为需要保护的数据。此外,环境变量沿着进程树传播,包括跨安全边界(例如 setuid/setgid 可执行文件),因此可能会泄漏到不应访问秘密数据的进程。使用 LoadCredential=、LoadCredentialEncrypted= 或 SetCredentialEncrypted=(见下文)将数据安全地传递到单元进程。

\n
\n