Passing variables into jsonnet without exposing them to the command line

652 views
Skip to first unread message

Charles Duffy

unread,
Jul 21, 2023, 11:58:23 AM7/21/23
to Jsonnet
Howdy --

The jsonnet specification says "it is possible to pass data from the environment, but only explicitly, by using abstractions provided by Jsonnet". Am I correct to understand that set of abstractions to be limited such that it _only_ includes the command line?

This is unfortunate: on UNIXy systems, command-line arguments are world-readable; if there's no other way to pass variables in, jsonnet can't safely be used to write secrets into generated files.

Consider something like jq's `env` to get access to environment variables (which on modern systems are accessible only to processes running as the same user or to root). It would be reasonable to require variables to have a known prefix (`jsonnet_varname` to expose `varname`) to ensure that exposure is intentional, should that be a concern; alternately, it would be reasonable to have a command-line argument like `--ext-str-from-env varname` to directly copy varname from the operating system's environment into jsonnet's set of available external variables.

Brett Viren

unread,
Jul 21, 2023, 12:26:53 PM7/21/23
to Charles Duffy, Jsonnet
Hi Charles,

I guess if you have secrets in environment variables, and you are
worried about them leaking via Unix process listing, then you do not
type "export foo=bar" on the command line and instead the secrets
ultimately reside in some shell rc file (eg .profile).

If that is true, then placing secrets in files is inside your threat
model. And then, you can bring secrets into Jsonnet via files in a
few ways.

You can import a single file with a hard-wired file name that you
treat as transient:

echo '"foo"' > secret.txt ; jsonnet -e 'local s = import "secret.txt";
s'; rm -f secret.txt

Instead of "echo" you might use eg the "pass" program or some other
CLI decryptor. That would be more secure than relying on env var
settings in .profile files.

Or, if you have many secrets, you might find it more convenient to
maintain a json/yaml file holding them all as an object, import that
file and then provide an object attribute key name of the particular
secret(s) needed by the Jsonnet code.

You can also combine the decryptor+hard-wired import and the
structured data of these two approaches via a named pipe. Well, I
didn't test, but I guess that should work.

-Brett.
> --
> You received this message because you are subscribed to the Google Groups "Jsonnet" group.
> To unsubscribe from this group and stop receiving emails from it, send an email to jsonnet+u...@googlegroups.com.
> To view this discussion on the web visit https://groups.google.com/d/msgid/jsonnet/1e452cdd-640a-4efb-8071-85044ba6a5e5n%40googlegroups.com.

Charles Duffy

unread,
Jul 21, 2023, 12:56:53 PM7/21/23
to Brett Viren, Jsonnet
Actually, `export foo=bar` on the command line is safe for purposes of the attack under discussion: `export` isn't an external command, so it never shows up in `ps`.

That said, as long as jsonnet supports FIFOs/named pipes and has `import`, yes, that's a great solution; using `--ext-str-from-env secret_file <(printf '%s\n' "$secret")` is safe (because printf is a builtin it doesn't get its own argv) and transforms, typically, to a `/dev/fd/##` filename that can be read from to consume the secret.

Brett Viren

unread,
Jul 21, 2023, 1:42:39 PM7/21/23
to Charles Duffy, Jsonnet
On Fri, Jul 21, 2023 at 12:56 PM Charles Duffy <cha...@dyfis.net> wrote:
>
> Actually, `export foo=bar` on the command line is safe for purposes of the attack under discussion: `export` isn't an external command, so it never shows up in `ps`.

Ah, right. Thanks!

>
> That said, as long as jsonnet supports FIFOs/named pipes and has `import`, yes, that's a great solution; using `--ext-str-from-env secret_file <(printf '%s\n' "$secret")` is safe (because printf is a builtin it doesn't get its own argv) and transforms, typically, to a `/dev/fd/##` filename that can be read from to consume the secret.

Perhaps another kludgey idea, but in principle you can preprocess the
Jsonnet file to replace some placeholder token with the secret value
and pipe the result into jsonnet's stdin.

-Brett.

Jeroen Op 't Eynde

unread,
Jul 24, 2023, 7:04:58 AM7/24/23
to Brett Viren, Charles Duffy, Jsonnet
You can do this and skip the file write completely: 

echo "secret" | jsonnet -e 'importstr "/dev/stdin"'

--
You received this message because you are subscribed to the Google Groups "Jsonnet" group.
To unsubscribe from this group and stop receiving emails from it, send an email to jsonnet+u...@googlegroups.com.


--
Jeroen Op 't Eynde
Message has been deleted

Angus Lees

unread,
Aug 13, 2023, 8:26:58 PM8/13/23
to Charles Duffy, Brett Viren, Jsonnet
Actually, `export foo=bar` on the command line is safe for purposes of the attack under discussion: `export` isn't an external command, so it never shows up in `ps`.

Erm.  See ps "e" option.

Right, "export" is a builtin, but the environment of a process is public. So you can't observe "export" executing, but you can see the result, both in the shell immediately, and in any child processes that inherit that same environment.



--
 - Gus

Charles Duffy

unread,
Aug 13, 2023, 8:31:14 PM8/13/23
to Angus Lees, Brett Viren, Jsonnet
ps e only shows the environment of processes owned by the same user unless you're root -- and if you're root you can see into the memory space of other processes anyhow.

Angus Lees

unread,
Aug 13, 2023, 10:30:55 PM8/13/23
to Charles Duffy, Brett Viren, Jsonnet
Oh, good point, and I didn't research beyond confirming my memory of having done this before. That will teach me. :)

I usually avoid secrets in env vars for soft defense-in-depth reasons: the environment is propagated to all children (that's the point), and it's moderately common to dump the env to a log file or such for debugging purposes. Otoh, it's rare (in modern frameworks) to propagate open files outside the original process, and rare to dump the contents of arbitrary files to logs.
--
 - Gus

Dave Cunningham

unread,
Aug 14, 2023, 6:56:06 AM8/14/23
to Angus Lees, Charles Duffy, Brett Viren, Jsonnet
I don't think this hiding of the environment was a thing when I implemented this feature initially in 2014! 

You can actually pass ext vars and TLAs via environment variables like this: (and it's documented in --help)

FOO=1+1 jsonnet --ext-code FOO -e 'std.extVar("FOO")'

I think this may be discouraged in the docs for secrets due the public visibility of environment vars, but if that is no-longer true of environment variables maybe it should be best practice?

Reply all
Reply to author
Forward
0 new messages