11

I'm using systemd on debian jessie to control a service to which I'm feeding environment variables through the EnvironmentFile=/etc/default/myservice file

in this file I have a variable which is a public key

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

putting it like that does not please systemd which report an error (though doing a source in bash of the same file works correctly)

the documentation of systemd report that you can have multiline variable by ending each file with a \ but that it concatenate each line (so my program receive the whole under one line, which is no more a valid public key)

Is there a known way to preserve the end of line ? without resorting to hack like putting \n which i them 'interpret' in my application code ?

allan.simon
  • 3,886
  • 6
  • 35
  • 60
  • 2
    This seems correctly handled in recent versions of systemd and should need no escaping workarounds. Tested with the version shipped with Debian Buster. – liberforce Nov 08 '19 at 10:39
  • 1
    Was fixed in systemd v239 released in June 2018: https://github.com/systemd/systemd/pull/8471 – TalkLittle Sep 05 '22 at 20:01

5 Answers5

3

The claim in the 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. Like in sh, a multiline value surrounded by single or double quotes will turn into a multiline value in the environment; you don’t need \n and \t, which are meaningless in sh (without quotes, they are interpreted as n and t; within quotes, they are interpreted as \n and \t. They never become newline and tab).

And if you end a line with \ in double quotes or no quotes, then this is a line continuation, and the newline is discarded, just like in sh.

I opened PR 21908 to fix this documentation.

yonran
  • 18,156
  • 8
  • 72
  • 97
2

Thanks to @yonran that improved the documentation in his pull request: https://github.com/systemd/systemd/pull/21908

So the solution is quite easy, you just have to put the value between single quotes. Warning, this solution may only works in recent version of systemd (tested with version >= 250).

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

If I understand correctly the updated documentation, this is the only solution to obtain new lines (0x0A aka \n characters) inside an environment variable. And this is clearly impossible to insert a new line by using the \n characters sequence inside the key value.

But after further tests, using double quotes also works (systemd does not complain, at least with systemd version 250).

Here my test files:

  • /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)

To test it:

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

So I do not understand why it failed in your case (You may be using a too old / broken version of systemd)

benjarobin
  • 4,410
  • 27
  • 21
0

As you suspected, Systemd accepts \n inside environment variable definitions. You need not perform and special parsing, just add appropriate \ns where they are needed and escape the actual newlines. Systemd should handle the rest and turn them into a linefeed literal. In your case this would look something like this:

JWT_PUB_KEY="-----BEGIN FOO BAR KEY-----\n\
MIIBgjAcBgoqhkiG9w0BDAEDMA4ECKZesfWLQOiDAgID6ASCAWBu7izm8N4V\n\
2puRO/Mdt+Y8ceywxiC0cE57nrbmvaTSvBwTg9b/xyd8YC6QK7lrhC9Njgp/\n\
...\n\
-----END FOO BAR KEY-----"
Vality
  • 6,577
  • 3
  • 27
  • 48
  • let me test that again, I think I've tested that combination, because I ended up with `\\n\` but it's been a long time so I may have overlooked – allan.simon Aug 04 '16 at 12:18
  • \\n would result in parsing the character '\' and parsing the character 'n' instead of the newline character '\n' – Daniel Kravetz Malabud Aug 05 '16 at 18:40
  • 1
    @Vality, actually it doesn't work, when I do that, and then I print in python I got the whole variable in one line – allan.simon Aug 25 '16 at 09:44
  • for my very specific case, actually I solved this now in a better way by using the public key in ssh format, which is a one-liner, so no problem. – allan.simon Oct 30 '17 at 15:04
  • 3
    @allan.simon Why did you accept this answer if it doesn't work? – Erik B Jul 14 '20 at 15:19
  • @Vality for me '\n\' ends up adding the letter n instead of a newline, which means that the certs don't work. – Erik B Jul 14 '20 at 15:30
0

You shouldn't have to interpret (parse?) the newline characters on your end, that should all happen automatically, as explained here , meaning that adding '\n' at the end of each line should do the trick.

0

Depending on your systemd version, you cannot use the above answers. (you can check the version by systemctl --version.) Actually any versions before this PR was merged should not be able to use multiline environment variables. For example, Amazon Linux 2 seems to use systemd v219, which was released on 2015/02.

Manually upgrading systemd is usually not recommended (ref), so there are not many things you can do about this issue.

As a workaround, I encoded the value in base64 and decoded it in the application.

# encode
# you need -w 0 option to wrap newlines
echo "YOUR_MULTILINE_VARIABLE" | base64 -w 0
tmokmss
  • 173
  • 1
  • 7