systemd,环境文件中的多行变量,其中新行很重要

systemd, multiline variable in environmentfile , where new line is significant

我在 debian jessie 上使用 systemd 来控制我通过 EnvironmentFile=/etc/default/myservice 文件向其提供环境变量的服务

在这个文件中我有一个变量,它是一个 public 键

  JWT_PUB_KEY="-----BEGIN FOO BAR KEY-----
  MIIBgjAcBgoqhkiG9w0BDAEDMA4ECKZesfWLQOiDAgID6ASCAWBu7izm8N4V
  2puRO/Mdt+Y8ceywxiC0cE57nrbmvaTSvBwTg9b/xyd8YC6QK7lrhC9Njgp/
  ...
  -----END FOO BAR KEY-----"

这样说并不能取悦 systemd,它会报告错误(尽管在同一文件的 bash 中执行 source 可以正常工作)

systemd 的文档报告说,您可以通过以 \ 结束每个文件来拥有多行变量,但它 concatenate 每行(所以我的程序接收整个在一行下,这不再是有效的 public 键)

是否有已知的方法来保留行尾?无需像将 \n 我他们 'interpret' 放在我的应用程序代码中这样的 hack ?

如您所料,Systemd 接受 \n 内部环境变量定义。您不需要执行特殊解析,只需在需要的地方添加适当的 \n 并转义实际的换行符。 Systemd 应该处理其余部分并将它们转换为换行文字。在您的情况下,这看起来像这样:

JWT_PUB_KEY="-----BEGIN FOO BAR KEY-----\n\
MIIBgjAcBgoqhkiG9w0BDAEDMA4ECKZesfWLQOiDAgID6ASCAWBu7izm8N4V\n\
2puRO/Mdt+Y8ceywxiC0cE57nrbmvaTSvBwTg9b/xyd8YC6QK7lrhC9Njgp/\n\
...\n\
-----END FOO BAR KEY-----"

你不应该解释(解析?)你最后的换行符,这一切都应该自动发生,as explained here,这意味着在每行的末尾添加'\n'应该可以诀窍。

systemd.exec documentation that within the EnvironmentFile, “C escapes are supported, but not most control characters. "\t" and "\n" can be used to insert tabs and newlines within EnvironmentFile=.” is completely false. Instead, the allowed quotes and escapes are the same as in a POSIX shell 中的声明。就像在 sh 中一样,用单引号或双引号括起来的多行值将在环境中变成多行值;你不需要 \n\t,它们在 sh 中没有意义(没有引号,它们被解释为 nt;在引号中,它们被解释为 \n\t。它们永远不会变成换行符和制表符)。

并且如果你以 \ 结束一行,用双引号或没有引号,那么这是一个续行,换行符被丢弃,就像在 sh.

中一样

我打开 PR 21908 来修复此文档。

感谢@yonran 改进了他的拉取请求中的文档:https://github.com/systemd/systemd/pull/21908

因此解决方案非常简单,您只需将值放在单引号之间即可。警告,此解决方案可能仅适用于最新版本的 systemd(测试版本 >= 250)。

JWT_PUB_KEY='-----BEGIN FOO BAR KEY-----
MIIBgjAcBgoqhkiG9w0BDAEDMA4ECKZesfWLQOiDAgID6ASCAWBu7izm8N4V
2puRO/Mdt+Y8ceywxiC0cE57nrbmvaTSvBwTg9b/xyd8YC6QK7lrhC9Njgp/
...
-----END FOO BAR KEY-----'

如果我对更新后的文档的理解正确,这是在环境变量中获取新行(0x0A 又名 \n 字符)的唯一解决方案。 而这显然不可能通过在键值中使用\n字符序列来插入一个新行。

但经过进一步测试,使用双引号也可以工作(systemd 不会抱怨,至少对于 systemd 版本 250)。

这是我的测试文件:

  • /etc/test-env.txt
JWT_PUB_KEY="-----BEGIN FOO BAR KEY-----
MIIBgjAcBgoqhkiG9w0BDAEDMA4ECKZesfWLQOiDAgID6ASCAWBu7izm8N4V
2puRO/Mdt+Y8ceywxiC0cE57nrbmvaTSvBwTg9b/xyd8YC6QK7lrhC9Njgp/
-----END FOO BAR KEY-----"
  • /etc/systemd/system/test-env.service
[Unit]
Description=Test env

[Service]
Type=simple
ExecStart=/usr/bin/test-dump-env.py JWT_PUB_KEY
EnvironmentFile=/etc/test-env.txt
  • /usr/bin/test-dump-env.py
#!/bin/env python3

import os, sys

s = os.environ.get(sys.argv[1])
h = ":".join("{:02x}".format(ord(c)) for c in s)

print(s)
print("***---***")
print(h)

测试一下:

systemctl restart test-env.service; sleep 1; systemctl status -l test-env.service

所以我不明白为什么它在你的情况下失败了(你可能使用的 systemd 版本太旧/损坏)