[HN Gopher] Environment variables are a legacy mess: Let's dive ...
       ___________________________________________________________________
        
       Environment variables are a legacy mess: Let's dive deep into them
        
       Author : signa11
       Score  : 200 points
       Date   : 2025-10-13 16:49 UTC (6 hours ago)
        
 (HTM) web link (allvpv.org)
 (TXT) w3m dump (allvpv.org)
        
       | rsyring wrote:
       | Environment variables are often used to pass secrets around. But,
       | despite its ubiquity, I believe that's a bad practice:
       | 
       | - On Linux systems, any user process can inspect any other
       | process of that same user for it's environment variables. We can
       | argue about threat model but, especially for a developer's
       | system, there are A LOT of processes running as the same user as
       | the developer.
       | 
       | - IMO, this has become an even more pronounced problem with the
       | popularity of running non-containerized LLM agents in the same
       | user space as the developer's primary OS user. It's a secret
       | exfiltration exploiter's dream.
       | 
       | - Environment variables are usually passed down to other spawned
       | processes instead of being contained to the primary process which
       | is often the only one that needs it.
       | 
       | - Systemd exposes unit environment variables to all system
       | clients through DBUS and warns against using environment
       | variables for secrets[1]. I believe this means non-root users
       | have access to environment variables set for root-only
       | units/services. I could be wrong, I haven't tested it yet. But if
       | this is the case, I bet that's a HUGE surprise to many system
       | admins.
       | 
       | I think ephemeral file sharing between a secret managing process
       | (e.g. 1Password's op cli tool) and the process that needs the
       | secrets (flask, terraform, etc.) is likely the only solution that
       | keeps secrets out of files and also out of environment variables.
       | This is how Systemd's credentials system works. But it's far from
       | widely supported.
       | 
       | Any good solutions for passing secrets around that don't involve
       | environment variables or regular plain text files?
       | 
       | Edit: I think 1Password's op client has a good start in that each
       | new "session" requires my authorization. So I can enable that
       | tool for a cli sessions where I need some secrets but a rogue
       | process that just tries to use the `op` binary isn't going to get
       | to piggyback on that authorization. I'd get a new popup. But this
       | is only step 1. Step 2 is...how to share that secret with the
       | process that needs it and we are now back to the discussion
       | above.
       | 
       | 1:
       | https://www.freedesktop.org/software/systemd/man/latest/syst...
        
         | robertlagrant wrote:
         | > On Linux systems, any user process can inspect any other
         | process of that same user for it's environment variables. We
         | can argue about threat model but, especially for a developer's
         | system, there are A LOT of processes running as the same user
         | as the developer.
         | 
         | This is a very good point I'd never realised! I'm not sure how
         | you get around it, though, as if that program can even find a
         | credential and decrypt a file, if it runs as the user then
         | everything else can go find that credential as well.
        
           | rsyring wrote:
           | I added a note to my original post about how 1Password's cli
           | tool work. Access to the binary doesn't mean automatic access
           | to any authorized session. I think that's a good start. In my
           | case, I have it tied into my fingerprint reader, so if
           | something want's access to a secret using `op` I get a prompt
           | to authorize. If I haven't just called `op` myself, I know
           | it's a suspect request.
        
           | chasil wrote:
           | If a root exploit is leveraged, then /proc/*/environ of all
           | processes is visible to the adversary.
           | 
           | The classical alternative has been to store (FTP) credentials
           | in a .netrc file (also used by curl).
           | 
           | I have some custom code to pull passwords out of a SQLite
           | database.
           | 
           | For people who are really concerned with this, a "secrecy
           | manger" is more appropriate, such as Cyberark conjur and
           | summon, or Hashicorp Vault.
        
             | jkrejcha wrote:
             | If a full root exploit is leveraged, it's already game over
             | already, basically anything at that point is just going to
             | be rearranging deck chairs.
        
         | formerly_proven wrote:
         | --passphrase-fd was always the correct thing to do (gpg has had
         | it for ages), but support for that is very meager. I mean, you
         | can't even kubectl login using environment variables, you
         | pretty much have to pass tokens through command-line arguments
         | and we've known that's terrible for around forty-five years.
        
         | bbkane wrote:
         | Any good _cross-platform_ and easy ways to share secrets
         | without using environment variables?
        
           | zdc1 wrote:
           | Point to a file? E.g. `CONFIG_PATH=/etc/myapp/config.ini
           | /opt/myapp`
           | 
           | That being said, I still use env vars and don't plan on
           | stopping. I just haven't (yet?) seen any exploits or threat
           | models relating to it that would keep me up at night.
        
             | maccard wrote:
             | How do you get that file?
             | 
             | I also use env vars.
        
             | bbkane wrote:
             | Is that file more secure than the environment variables
             | it's replacing? On Linux I think you can secure it to just
             | your service with SELinux. Not sure about Windows
        
           | matthew16550 wrote:
           | SOPS can be part of the solution. It takes care of encrypting
           | and decrypting config files.
           | 
           | https://github.com/getsops/sops
        
         | skylurk wrote:
         | I've used keyring, but ultimately does not solve the issue of
         | every application running under the same user.
         | 
         | https://pypi.org/project/keyring/
        
         | cedws wrote:
         | Linux security model is pretty broken without namespaces.
         | systemd has some bells and whistles that help but if you want
         | something better than environment variables you're naturally
         | going to reach for cgroups.
        
           | throwaway127482 wrote:
           | cgroups aren't relevant here, I think. Not sure if that was
           | just a typo, since you did mention namespaces in the first
           | sentence. PID and user namespaces in particular are relevant
           | here.
        
             | cedws wrote:
             | Yeah seems you're right, I thought namespaces came under
             | the cgroups umbrella.
        
           | ux266478 wrote:
           | Linux's namespaces are not a security mechanism. Please do
           | not use them as one.
        
             | cedws wrote:
             | I've said exactly the same thing on HN myself. However for
             | this problem what do you suggest? Using namespaces is
             | better than using no namespaces.
        
               | ux266478 wrote:
               | If I was actually serious about the security of a system,
               | I'd explicitly use virtualization at-minimum. If all I
               | wanted was a mall cop and was happy to take the risk of
               | misconfiguration or attacks on the kernel I'd probably
               | say something that adds a lot of extra sanity to
               | namespaces is "good enough", like bubblewrap. I would
               | never trust it or say that it's secure though.
        
             | jcgl wrote:
             | Is there consensus around that? Given how containers and
             | bubblewrap/flatpak are considered (to some degree) security
             | boundaries, I approach statements like this with
             | skepticism.
        
               | ux266478 wrote:
               | You answered your own question. Bubblewrap uses
               | namespaces, but not by themselves. They're used in
               | conjunction with other tools to provide a security
               | boundary. Even then, it's not a very good security
               | boundary. A serious security boundary should hold up even
               | to privilege escalation, which is not true for
               | bubblewrap.
        
               | jcgl wrote:
               | Each kind of namespace provides its own kind of boundary.
               | Yes, you need something like bubblewrap to stitch that
               | all together. And it is kinda leaky, especially without
               | taking some degree of care.
               | 
               | What're you saying about privilege escalation? I don't
               | see how a user namespace does not prevent/limit privilege
               | escalation.
               | 
               | More than that, I'm interested if there is some broader
               | consensus I'm missing on the shortcomings of namespaces.
        
               | ux266478 wrote:
               | > I don't see how a user namespace does not prevent/limit
               | privilege escalation.
               | 
               | They only prevent a single class of privilege escalation
               | from setuid usage within the namespace. You can still
               | obtain root using race conditions, heap overflows, side-
               | channels, etc. or by coordinating with something outside
               | of the namespace like a typical chroot escape.
               | 
               | Here's an old (patched) example of escaping the sandbox:
               | 
               | https://lwn.net/Articles/543273/
               | 
               | > More than that, I'm interested if there is some broader
               | consensus I'm missing on the shortcomings of namespaces.
               | 
               | The consensus is that they're not security features on
               | Linux. I'm not sure who sold you on the idea that they
               | were, because that was not handed down by the kernel
               | devs.
               | 
               | https://lwn.net/Articles/657744/
        
           | 1718627440 wrote:
           | Linux/Unix security model is that different actors are
           | represented by different OS users. When you don't do that,
           | it's already like inviting the burglars in your living room.
        
             | oblio wrote:
             | Everybody does that. How many untrusted NPM dependencies
             | are installed as we speak? Chrome extensions? Etc.
        
               | 1718627440 wrote:
               | "Everybody" runs an operating system, where it is common
               | to install programs by downloading random files from the
               | internet and then clicking OK to any UAC prompt. Doesn't
               | mean that this is not insecure.
        
         | mhitza wrote:
         | > Any good solutions for passing secrets around that don't
         | involve environment variables or regular plain text files?
         | 
         | memfd_secret comes to mind https://man7.org/linux/man-
         | pages/man2/memfd_secret.2.html
         | 
         | I haven't seen much language support for it, though. On one
         | part maybe because it's Linux only.
         | 
         | People that write in Rust (and maybe Go, depends how easy FFI
         | is) should give it a try.
         | 
         | I wanted for a time to get some support for it in PHP, since
         | wrapping a C function should be easy, but the thought of having
         | to also modify php-fpm put a dent in that. I can't and don't
         | want to hack on C code.
         | 
         | In practice it'd be great if the process manager spawn children
         | after opening a secret file descriptor, and pass those on. Not
         | in visible memory, not in /proc/*/environ
        
           | llimllib wrote:
           | For go, it's available in /x/sys/unix:
           | https://pkg.go.dev/golang.org/x/sys/unix#MemfdSecret
        
           | monocasa wrote:
           | Oh, I like that.
           | 
           | You should be able to build up a nice capability model to get
           | access to those memfds from daemon too rather than having to
           | spawn out of a process manager if that model fits your use
           | case a bit better.
        
         | bjourne wrote:
         | If are like most users you have secrets stored in read-
         | protected files in ~/.ssh. There's nothing wrong with that.
        
           | SoftTalker wrote:
           | They are (should be) encrypted though, and not usable by
           | someone who might somehow gain access to your ~/.ssh files.
        
             | bjourne wrote:
             | No, the private keys are not encrypted. If there were,
             | where would you store the encryption key for decrypting
             | them?
        
         | amluto wrote:
         | Since at least 2012, environment variables have been at least
         | as secure as ordinary memory:                   commit
         | b409e578d9a4ec95913e06d8fea2a33f1754ea69         Author: Cong
         | Wang <xiyou.wangcong@gmail.com>         Date:   Thu May 31
         | 16:26:17 2012 -0700                      proc: clean up
         | /proc/<pid>/environ handling
         | 
         | You can't read another process's environment unless you can
         | ptrace-read the process, and if you can ptrace-read the process
         | you know all its secrets anyway.
         | 
         | cmdline is a different story.
        
           | rsyring wrote:
           | I've never tried to use ptrace-read to read secrets from a
           | running process's memory so I can't comment on that part.
           | 
           | I had always assumed getting secrets from a running process'
           | memory was non-trivial and/or required root permissions but
           | maybe that's a bad assumption.
           | 
           | However, reading a process' environment is as trivial as:
           | 
           | `cat /proc/123456/environ`
           | 
           | as long as it's ran by the same user. No ptrace-read
           | required.
        
             | intorio wrote:
             | You do need PTRACE access to pid 123456 in order to access
             | that file. It is transparent to you, but the kernel will
             | use the current task's PTRACE_ATTACH access when attempting
             | to get that information.
             | 
             | By default, on most distributions, a user has PTRACE_ATTACH
             | to all processes owned by it. This can be configured with
             | ptrace_scope:
             | 
             | https://www.kernel.org/doc/Documentation/security/Yama.txt
        
           | marcosdumay wrote:
           | Just to clarify, that means that by default every process of
           | the same user can access the variables. But it doesn't really
           | matter because by default every process of the same user can
           | read any secret directly from the target process anyway,
           | right?
           | 
           | And that the right thing to do if you want to harden your
           | system is to disallow ptrace-read, and not bother changing
           | software that uses environment variables?
           | 
           | Because I think most people that just try will be able to
           | read the variables of any process on their computer.
        
             | nine_k wrote:
             | Yes, you can consider all processes running under the same
             | user as able to peek at each other's data. This is _the
             | point_ of running under the same uid: sharing data.One uid
             | should be considered one security domain, with any
             | separators inside it being guardrails, not brick walls.
             | 
             | If you want to prevent other processes from peeking into
             | your process, run it under a different uid. Again. that's
             | the point. A bunch of good software does that, running only
             | small privileged bits in separate processes, and running
             | some / bulk of the processes under an uid with severely
             | limited privileges. See e.g. Postfix MTA, or the typical
             | API server + reverse proxy split.
             | 
             | I don't think that _this_ part of Unix is somehow
             | problematic, or needs changing.
        
               | amluto wrote:
               | I think this part of Unix is exceedingly problematic. I
               | want to be able to run program that don't have the
               | ability to do anything that I, personally, can do.
               | Ideally this would be doable without nasty kludges or
               | root's help.
               | 
               | Linux has some ways to accomplish this, for example:
               | 
               | - seccomp. It can be done quite securely, but running
               | general purpose software in seccomp is not necessarily a
               | good way to prevent it from acting like the running user.
               | 
               | - Namespaces. Unless user namespaces are in use, only
               | root can set this up. With user namespaces, anyone can do
               | it, but the attack surface against the kernel is huge,
               | mount namespaces are somewhat porous (infamously,
               | /proc/self/exe is a hole), and they can be a bit
               | resource-intensive, especially if you want to use network
               | namespaces for anything interesting. Also, user
               | namespaces have kind of obnoxious interactions with
               | ordinary filesystem uids (if I'm going to run some
               | complex containerized stack with access to a specific
               | directory within my home directory, who should own the
               | files in there, and how do I achieve that without root's
               | help?). And userns without subuid doesn't isolate very
               | strongly, and subuid need's root's help.
               | 
               | - Landlock. Kind of cool, kind of limited.
               | 
               | - Tools like Yama. On Ubuntu, with Yama enabled (the
               | default), programs can't generally ptrace (or read
               | /proc/self/environ) from other programs running as the
               | same user.
               | 
               | In any case, once you've done something to prevent a
               | process from accessing /proc/fd/PID for other pids
               | belonging to the same user (e.g. pidns) and/or restricted
               | ptrace (e.g. PR_SET_DUMPABLE), then environ gets
               | protected.
        
               | jkrejcha wrote:
               | It's not your (unprivileged user account's) place to
               | decide the security posture of the entire system, that's
               | why you're running into issues with root. Even Yama, an
               | LSM, requires root (or de facto equivalent) for initial
               | setup (as it should).
               | 
               | Namespaces, if done incorrectly, can significantly
               | increase the attack surface of the entire system (mount
               | namespaces especially need to be treated with care) and
               | same with regards to anything to do with user accounts.
               | 
               | The real security barrier on most operating systems to
               | date is the user account and if you want full isolation
               | (modulo kernel bugs), run a process as a separate user if
               | you're that concerned about leakage (or ideally, don't
               | run it at all).
        
               | amluto wrote:
               | > It's not your (unprivileged user account's) place to
               | decide the security posture of the entire system, that's
               | why you're running into issues with root.
               | 
               | Tell that to literally any unprivileged user who would
               | like to run any sort of software without exposing their
               | entire account to any possible code execution bug or
               | malicious code in the software they're running.
               | 
               | > The real security barrier on most operating systems to
               | date is the user account and if you want full isolation
               | (modulo kernel bugs), run a process as a separate user if
               | you're that concerned about leakage (or ideally, don't
               | run it at all).
               | 
               | That a very 1980s or maybe 1990s perspective, I think.
               | It's 2025. I have some computers. They are _mine_. I
               | trust myself to administer them and do pretty much
               | anything to them, but I would prefer not to trust all my
               | software. Sure, I can, very awkwardly, try to isolate
               | things by using multiple uids, but it 's extremely
               | awkward and I see no technical reason why uids are a good
               | solution here.
               | 
               | And there are no shortage of technical reasons why uids
               | are a horrible solution. I would like to run unprivileged
               | software and delegate some degree of privilege to them --
               | imagine a web browser or an IDE or whatever. I, as the
               | owner of the machine, can make a uid. But I would also
               | like my IDE to isolate various things it runs from each
               | other and from the IDE as a whole, and it cannot do that
               | because it does not own the machine and should absolutely
               | not be running as root.
        
               | icedchai wrote:
               | Apple's "App Sandbox" stuff helps quite a bit with this
               | sort of thing. I realize that doesn't solve the Linux /
               | generic Unix case.
               | https://developer.apple.com/documentation/security/app-
               | sandb...
        
               | nine_k wrote:
               | Yes, if we talk about interactive use, where the user may
               | want to run a program with isolation on a whim, and can't
               | be bothered to prepare a separate account for it.
               | 
               | Namespaces, to my mind, are a huge help, starting from
               | trivial chroot and nsenter, all the way to bubblewrap [2]
               | and containers (rootless, of course).
               | 
               | Also, with a properly prepared disk image, and the OS
               | state frozen when everything heavyweight has started
               | already, you can spawn a full-blown VM in well under a
               | second, and run a questionable binary in it.
               | 
               | It would be fun to have "domains" within a user account,
               | with their own views of the file system, network, process
               | tree, etc, and an easy way to interactively run programs
               | in different domains. Again, it's pretty doable; whoever
               | creates an ergonomic product to do it, will win! (Well,
               | not really, because most developers sadly run macOS,
               | which lacks all these niceties, but has its own
               | mechanisms, not very accessible to the user.)
               | 
               | [1]: https://quintessence.sh/blog/bwrap-guide-linux/
        
         | vendiddy wrote:
         | Not an answer, but I do wish there was a low level primitive
         | and a corresponding high level language construct to pass
         | around secrets.
         | 
         | Something like: my_secret = create_secret(value)
         | 
         | Then ideally it's an opaque value from that point on
        
           | 1718627440 wrote:
           | my_secret = getenv ("VALUE");
           | 
           | :-)
        
           | jkrejcha wrote:
           | Until when? Secrets in applications in many cases (I would
           | probably wager majority of the cases) are only useful if
           | they're in plaintext at some point, for example if you're
           | constructing a HTTP client or authenticating to some other
           | remote system.
           | 
           | As far as high-level language constructs go, there were
           | similarish things like SecureString (in .NET) or
           | GuardedString (in Java), although as best as I can tell
           | they're relatively unused mostly because the ergonomics
           | around them make them pretty annoying to use.
        
         | motorest wrote:
         | > Environment variables are often used to pass secrets around.
         | But, despite its ubiquity, I believe that's a bad practice:
         | 
         | I think environment variables are recommended to pass
         | configuration parameters, and also secrets, in containerized
         | applications managed by container orchestration systems.
         | 
         | By design, other processes cannot inspect what environment
         | variables are running in a container.
         | 
         | Also, environment variables are passed to child processes
         | because, by design, the goal is to run child processes in the
         | same environment (i.e., same values) as the parent process, or
         | with minor tweaks. Also, the process that spawns child
         | processes is the one responsible for set it's environment
         | variables, which means it already has at least read access to
         | those secrets.
         | 
         | All in all I think all your concerns are based on specious
         | reasoning, but I'll gladly discuss them in finer detail.
        
           | rsyring wrote:
           | Please note that in my OP, I never mentioned containers.
           | 
           | Let's say, as a developer, I need to do some API interactions
           | with GitHub. So, in a terminal, using 1Password's cli tool
           | `op`, I load my GH API token and pass it to my Python script
           | that is doing the API calls.
           | 
           | Presumably, the reason I use that process is because I want
           | to keep that token's exposure isolated to that script and I
           | don't want the token exposed on the filesystem in plaintext.
           | There is no reason for every process running as my user on my
           | laptop to have access to that token.
           | 
           | But, that's not how things work. The Claude agent running in
           | a different CLI session (as an example) also now has access
           | to my GitHub token. Or, any extension I've ever loaded in VS
           | Code also has access. Etc.
           | 
           | It's better than having it in plain text on the file system
           | but not by much.
           | 
           | Additionally, unless I've misunderstood the systemd docs, if
           | you are passing secrets through environment variables using
           | unit's `Environment` config, ANY USER on a server can read
           | those values. So now, we don't even have user isolation in
           | effect.
           | 
           | My reasoning is pretty plain. It could be wrong, but it's
           | hardly specious.
        
             | imiric wrote:
             | > The Claude agent running in a different CLI session (as
             | an example) also now has access to my GitHub token. Or, any
             | extension I've ever loaded in VS Code also has access.
             | 
             | If you're giving untrusted software full access to your
             | system, you're not in a position to complain about the
             | system not being secure enough. Security starts by making
             | mindful decisions according to your threat model. No system
             | can keep you secure from yourself. I mean, it _can_ , but
             | it wouldn't be a system you have control over (see Apple,
             | etc.).
             | 
             | There are many solutions that can mitigate the risks you
             | mention. They might not be as convenient as what you're
             | doing now, but they exist. You can also choose to not use
             | software that siphons your data, or exposes you to
             | countless vulnerabilities, but that's a separate matter.
        
           | rustyminnow wrote:
           | I think in the context of containers you're right, there's a
           | level of isolation and secrets are probably fine. But I think
           | under other contexts that lack that isolation (e.g. bare-
           | metal processes, local dev tooling) there are extra concerns.
           | 
           | (inb4: container env-vars are isolated from other containers,
           | not from processes on the host system)
        
           | openasocket wrote:
           | > By design, other processes cannot inspect what environment
           | variables are running in a container.
           | 
           | That's not exactly true. If a process is running in a
           | container, and someone is running bash outside of that
           | container, reading that processes environment variables is as
           | simple as "cat /proc/<pid>/environ". If you meant that
           | someone in one container cannot inspect the environment
           | variables of a process running in a different container,
           | that's more true. That said, containers should not be
           | considered a security boundary in the same way a hypervisor
           | is.
        
         | jkrejcha wrote:
         | > - On Linux systems, any user process can inspect any other
         | process of that same user for it's environment variables. We
         | can argue about threat model but, especially for a developer's
         | system, there are A LOT of processes running as the same user
         | as the developer.
         | 
         | Here's the thing though, the security model of most operating
         | systems means that running a process as a user is acting as
         | that user. There are some caveats (FreeBSD has capsicum, Linux
         | has landlock, SELinux, AppArmor, Windows has integrity labels),
         | but in the general case, if you can convince someone to run
         | something, the program has delegated authority to act on behalf
         | of that user. (And some user accounts on many systems may have
         | authority to impersonate others.)
         | 
         | While it's by no means the only security model (there exists
         | fully capability based operating systems out there), it's the
         | security model that is used for most forms of computing, for
         | better or worse. One of the consequences of this is that you
         | can control anything within your domain. I can kill my own
         | processes, put them to sleep, and most importantly for this,
         | debug them. Anything I own that has secrets can grab them my
         | ptrace/process_vm_readv/ReadProcessMemory/etc.
        
         | Narushia wrote:
         | > Any good solutions for passing secrets around that don't
         | involve environment variables or regular plain text files?
         | 
         | Honestly, my answer is still systemd-creds. It's easy to use
         | and avoids the problem that plain environment variables have.
         | It's a few years old by now, should be available on popular
         | distros. Although credential support for user-level systemd
         | services was added just a few weeks ago.
         | 
         | A TL;DR example of systemd-creds for anyone reading this:
         | # Run the initial setup         systemd-creds setup
         | # This dir should have permissions set to 700 (rwx------).
         | credstore_dir=/etc/credstore.encrypted         # For user-level
         | services:         #
         | credstore_dir="$HOME/.config/credstore.encrypted"
         | # Set the secret.         secret=$(systemd-ask-password -n)
         | # Encrypt the secret.         # For user-level services, add
         | `--user --uid uidhere`.         # A TPM2 chip is used for
         | encryption by default if available.         echo "$secret" |
         | systemd-creds encrypt \             --name mypw -
         | "$credstore_dir/mypw.cred"         chmod 600
         | "$credstore_dir/mypw.cred"
         | 
         | You can now configure your unit file, e.g.:
         | [Service]
         | LoadCredentialEncrypted=mypw:/etc/credstore.encrypted/mypw.cred
         | 
         | The process you start in the service will then be able to read
         | the decrypted credential from the ephemeral file
         | `$CREDENTIALS_DIR/mypw`. The environment variable is set
         | automatically by systemd. You can also use the command
         | `systemd-creds cat mypw` to get the value in a shell script.
         | 
         | At least systemd v250 is required for this. v258 for user-level
         | service support.
        
         | mixmastamyk wrote:
         | You've described the classic Unix security model, with minor
         | improvements. While decent for the time it is showing its age.
         | Specifically the difficulty in adapting to cheap, ubiquitous
         | computing which it wasn't designed for.
         | 
         | If you need to keep secrets from other processes--don't run
         | them under the same user account. Or access them remotely,
         | although that brings other tradeoffs and difficulties.
        
         | lelanthran wrote:
         | > Any good solutions for passing secrets around that don't
         | involve environment variables or regular plain text files?
         | 
         | Plain text files are fine, it's the permissions on that file
         | that is a problem.
         | 
         | The best way is to be in control of the source to the program
         | you want to run then you can make a change to always protect
         | against leaking secrets by starting the program as a user that
         | can read the secrets field. After startup the program reads the
         | whole file and immediately drops privileges by switching to a
         | user that cannot read the secrets file.
         | 
         | Works for more than just secrets too.
        
       | simonask wrote:
       | Interesting read. Another interesting fact is that `setenv()` is
       | fundamentally broken on POSIX, and should essentially never be
       | called in library code. In application code, it should be called
       | only in the absence of any alternative, and certainly before any
       | threads have started.
       | 
       | The reason is that `getenv()` hands out raw pointers to the
       | variables, so overwriting a variable using `setenv()` is
       | impossible to guard against. Treat with extreme caution.
        
         | wmf wrote:
         | AFAIK Solaris solved that problem but Linux refuses to copy
         | their solution.
        
           | foofoo12 wrote:
           | Link?
        
           | ori_b wrote:
           | Their solution was that setenv leaks memory rather than
           | overwriting in place.
           | 
           | FreeBSD does the same. See here for discussion:
           | https://freebsd-current.freebsd.narkive.com/NwqZQDWm/fix-
           | for...
        
             | monocasa wrote:
             | Sure. But practically the amount you leak is infinitesimal.
             | 
             | For a lot of applications that's the right call given the
             | rest of the posix semantics you're constrained to and the
             | kinds and frequency of data you pass via env vars.
        
               | cbsmith wrote:
               | An infinitesimal leak becomes a problem if it is done an
               | infinite number of times...
               | 
               | execve seems like the preferable choice on a lot of
               | grounds.
        
               | dwattttt wrote:
               | It would be a problem, except that the behaviour you're
               | moving away from is a stale pointer. So surely any
               | application that'd be leaking under that new behaviour
               | would be crashing today.
        
               | ori_b wrote:
               | The same advice applies to a correct program regardless
               | of the implementation: using setenv is a bug. Only use
               | getenv.
               | 
               | Getenv in a program without setenv is fine in both
               | implementations. Setenv is unusable with all conforming
               | implementations.
               | 
               | To pass environments to children, use execve.
               | 
               | The Linux behavior allows a careful single threaded
               | program to use setenv correctly. The BSD/Solaris behavior
               | makes all usage incorrect, but the incorrectness comes in
               | the form of a memory leak, which is preferable to a
               | security issue, usually.
               | 
               | There's no correct, portable use of setenv. If you call
               | it, it's a bug.
        
               | 1718627440 wrote:
               | No it always leaks, regardless if your program now
               | correctly invalidates all derived pointers upon calling
               | setenv.
        
               | monocasa wrote:
               | Which is why I said
               | 
               | > frequency of data
               | 
               | Practically, if you're moving enough data through setenv
               | that the memory leaked versus the steady state
               | fluctuation of the program is at all visible, you've got
               | much bigger problems.
        
           | Analemma_ wrote:
           | IIRC the Solaris implementation of getenv()/setenv() leaks
           | memory, because they want it to still be POSIX-compatible and
           | the POSIX API has a "obey the spec, don't leak memory, be
           | thread-safe -- pick 2" thing going on and you can't actually
           | have a bug-free solution that keeps compatibility with
           | decades of Unix applications.
           | 
           | Arguably that's less harmful than the thread-safety issues on
           | Linux, but there's no perfect solution here as long as POSIX
           | stays what it is.
        
         | nrds wrote:
         | I think it's worth specifically calling out that the right way
         | to set environment variables is in execve() only, as
         | communication across an exec() is the precise niche for which
         | they are the right tool.
        
           | sureglymop wrote:
           | Why are they the right tool for that instead of passing data
           | with program args or another way of IPC?
        
             | fullstop wrote:
             | Program args can be seen with tools like 'ps', so passing
             | credentials that way is a poor choice.
        
               | sureglymop wrote:
               | Well yeah of course. What you could do though would be to
               | have e.g. --secrets-file and at startup time the
               | application reads that file to get the secret. Then you
               | could use file permissions to make sure only the
               | (application) user running the application can read that
               | file (or even more extreme, the application destroys the
               | file after reading it).
               | 
               | I think that would still be better than env vars, which
               | are more likely to leak somewhere you didn't intend them
               | to.
        
               | hinkley wrote:
               | They can also be caught by bash and system audit logs.
        
               | darrenf wrote:
               | FWIW, environment variables (edit: of your own processes)
               | can also be seen with `ps`.                    ps wwwex |
               | grep [w]wwex           31109 pts/0    R+     0:00 ps
               | wwwex GDM_LANG=en_GB.utf8 STARSHIP_SHELL=fish
               | GDMSESSION=xfce STARSHIP_SESSION_KEY=2904922223926273
               | XDG_CURRENT_DESKTOP=XFCE LC_NUMERIC=en_GB.UTF-8
               | TERMINFO=/usr/share/terminfo LC_MONETARY=en_GB.UTF-8
               | SHELL=/bin/fish LC_ADDRESS=en_GB.UTF-8 ... many, many
               | more ...
        
               | fullstop wrote:
               | Right, you can see your own environment variables. Root
               | can see your environment variables. Another user can not.
               | 
               | edit: submitted before I saw your edit. :-)
        
         | skissane wrote:
         | NetBSD has had getenv_r() for ages, [0] and I believe FreeBSD
         | has very recently copied it. [1] If FreeBSD has it, macOS might
         | eventually adopt it too. People tried already to get it into
         | both glibc and POSIX, and it was rejected, but if it becomes
         | more common on other platforms, that increases the chance
         | they'll both eventually accept it.
         | 
         | [0] https://man.netbsd.org/getenv_r.3
         | 
         | [1] https://github.com/freebsd/freebsd-
         | src/commit/873420ca1e6e8a...
        
           | layer8 wrote:
           | You still don't know if some library you use calls getenv()
           | and stores the returned pointer (or, potentially, runs in a
           | different thread). Using getenv_r() protects your code
           | against setenv(), but other code that may be using getenv()
           | is still unprotected against your (or anyone else's) use of
           | setenv().
        
             | skissane wrote:
             | > You still don't know if some library you use calls
             | getenv()
             | 
             | Well, you can know, if you check their imported symbols. (I
             | suppose they could be getting it via dlsym, but what are
             | the odds of that...)
             | 
             | So if getenv_r() is added to the C library, and over time
             | third party libraries started adopting it, you could get to
             | the point that you could know no code in your process is
             | calling getenv(), because none of the libraries your
             | process loads import that symbol.
             | 
             | They could even add a glibc tunable to make getenv() call
             | abort()... then you could be very sure nobody is calling
             | getenv(), because if anyone ever did, _boom_
        
         | Ferret7446 wrote:
         | Why would you ever call setenv in library code to begin with?
        
       | shmerl wrote:
       | _> POSIX-specified utilities use uppercase envvars, but that's
       | not prescriptive for your programs. Quite the contrary: you're
       | encouraged to use lowercase for your envvars so they don't
       | collide with the standard tools._
       | 
       |  _> But in reality, not many applications use lowercase. The
       | proper etiquette in software development is to use ALL_UPPERCASE_
       | 
       | I always prefer lower case for env variables in scripts. Thanks
       | for pointing out that it can help reduce clashes with standard
       | tools.
        
       | amelius wrote:
       | Another legacy mess:                   Argument list too long
       | 
       | It's absolutely crazy that this isn't a dynamically resizable
       | vector.
        
         | formerly_proven wrote:
         | It is, there's just a limit set by the kernel for the number of
         | pages the command line as a whole can occupy or something like
         | that.
        
           | amelius wrote:
           | No, it's still an artificial limit, even if you can change it
           | (usually after your command has failed).
        
         | chasil wrote:
         | The xargs command was designed to address this. From this
         | perspective, it is a kludge.
        
       | the__alchemist wrote:
       | I get anxiety whenever I need to set an environment var on Linux.
       | There are (somewhat distro-specific) ways that work properly, but
       | the usual procedures you find online stops working once you
       | reboot (or close the terminal I think?). They should add a simple
       | env var GUI like Windows has that _just works_ , and isn't
       | terminal-specific. Windows has the annoyance of needing to
       | restart the the terminal (or open a new one) for changes to take
       | effect, but works well other than that.
        
         | zdragnar wrote:
         | > but the usual procedures you find online stops working once
         | you reboot (or close the terminal I think?)
         | 
         | The environment isn't persistent between sessions. That means
         | you need to make the change in a way that runs on every new
         | session (login or new terminal window).
         | 
         | Depending on how your system is configured:
         | 
         | .bash_profile gets run once on every login
         | 
         | .bashrc gets run once on every non-login new session (i.e. a
         | new terminal window)
         | 
         | It's typical, if using these files, to do something like this:
         | if [ -f ~/.bashrc ]; then             source ~/.bashrc
         | fi
         | 
         | in the .bash_profile file, putting most other things in
         | .bashrc, such that you don't have to worry about the
         | distinction.
         | 
         | If you're not even using bash or bash-likes at all, but instead
         | something like Zsh, fish, etc you'll need to set things the way
         | they want to be set there.
         | 
         | > They should add a simple env var GUI like Windows has that
         | just works, and isn't terminal-specific
         | 
         | This doesn't exist in linux, because there isn't "one place"
         | that all possible terminals draw from. Conceivably, it's
         | possible to write a GUI tool that reads your .bashrc file,
         | looks for anything that resembles a variable, parses the bash
         | code to look for ways it is set, and then present a GUI tool
         | that could update it in common cases, but... it's way easier to
         | just write the change in a text editor.
        
           | petre wrote:
           | > Conceivably, it's possible to write a GUI tool that reads
           | your .bashrc file
           | 
           | What's wrong with an envfile or envdir? The envdir is kind of
           | annoying but at least you can set permissions on the files
           | inside it.
        
             | zdragnar wrote:
             | A lot of programs and tools will try to be cute and write
             | into your bash files for you. I was mostly waxing
             | philosophical at that point having gotten nerd sniped by
             | "write a gui tool for environment variables" and
             | "environment variables like $PATH in bash files" coming
             | together in my head.
        
           | SAI_Peregrinus wrote:
           | That applies if you use bash. If you use another shell,
           | you'll need to use that shell's methods to set environment
           | variables in the config(s). And probably still need to set
           | them for bash so that bash scripts get the correct env vars.
           | 
           | NixOS & home-manager do have functions that can set
           | environment variables across all shells enabled via either of
           | those systems. So I've got `programs.bash.enable = true;` &&
           | `programs.fish.enable = true;` && `home.sessionVariables = {
           | EDITOR = "nvim"; };` in my home-manager config for example. A
           | GUI editor for that would certainly be possible.
        
         | jcelerier wrote:
         | Funny, as a primarily linux user the windows behaviour irks me
         | to no end, it's the cause of so many recurrent problems on end-
         | user machines with so many apps that pollute environment.. and
         | then you wonder why something doesn't work and then it turns
         | out that for some reason you're using $SOFTWARE from
         | c:\Perl64\bin instead of its proper place
        
         | NekkoDroid wrote:
         | On systemd systems you can just either set KEY=VALUE pairs in
         | `/etc/environment` or any file in `/etc/environment.d/` (and
         | technically a few other places [0]). In theory it should be
         | relatively easy to write a GUI for it by manually parsing the
         | files.
         | 
         | The application restarting part can't really be fixed, since
         | environment variables aren't ever injected to a running process
         | and can only be changed by the process itself (terms and
         | conditions may apply) and even changes during runtime could be
         | ignored since the program itself may have cached some already
         | computed value based on the variable.
         | 
         | [0]:
         | https://www.freedesktop.org/software/systemd/man/latest/envi...
        
           | the__alchemist wrote:
           | Good info. I may write a script or tiny program to help with
           | this.
        
         | csours wrote:
         | https://xkcd.com/927/ - Standards
         | 
         | Situation: There are 14 competing ways to set environment
         | variables on Linux
         | 
         | We should create a universal way that just works and isn't
         | terminal specific to set environment variables!
         | 
         | Situation: There are 15 competing ways to set environment
         | variables on Linux
        
       | bjourne wrote:
       | We're almost rid of dotfiles in $HOME. Can we please also get rid
       | of the abuse of environment variables for configuration? There is
       | no reason your program needs to muck up the environment table of
       | every process by defining MY_PROGRAM_API_KEY instead of taking a
       | command line argument or reading a configuration file. It's not
       | "secure" just because it's not on the command line. And it will
       | mess up for users because it's nigh impossible to ensure you have
       | the same environment variables over all login types. The variable
       | might be there when you run locally, but not in an ssh session or
       | cron. Some ephemeral configuration such as LD_LIBRARY_PATH and
       | PWD is difficult to handle without environment variables, but
       | those are rare _exceptions_ and not the norm.
       | 
       | Contribute to a clean environment! Don't use environment
       | variables for configuration! </rant>
        
         | 1718627440 wrote:
         | This problem is also removed by just not exporting the
         | variables. Also you can just pass an envp to execvE.
        
           | bjourne wrote:
           | "MY_APP_SECRET_KEY=bla myapp" hardly accomplishes anything
           | substantial over "myapp --secret-key=blah" It's just a less
           | robust and less well-supported command-line interface. It's
           | not supported by most gui launchers, PowerShell, nor many
           | cron implementations, for example.
        
             | akvadrako wrote:
             | The command line can be read by any user on the host (with
             | `ps auxww` for example) while the environment cannot.
             | 
             | You should never pass secrets on the command line.
        
               | bjourne wrote:
               | Environment variables are _not more secure_ than command
               | line parameters! It 's such a common misconception that
               | because environment variables are "not seen" they can
               | serve as a secure channel. They emphatically cannot.
        
         | phito wrote:
         | They're the standard for Docker container configuration tho.
         | Otherwise I do agree.
        
         | mixedbit wrote:
         | Depending on your system, passing secrets via environment can
         | be more secure than passing secrets via a command line. Command
         | line arguments of all running processes can be visible to other
         | users of the system, environment variables are usually not.
        
       | aleksandrm wrote:
       | That was a hard to read font.
        
         | lemontheme wrote:
         | Huh... Guess it's what you're used to.
         | 
         | First thing that struck me about the site is how beautiful I
         | found it. I even inspected the font: Iosevka Web, apparently.
        
         | db48x wrote:
         | Change your browser settings. You can tell the browser what
         | font you prefer to use, and set minimum font sizes. You can
         | also turn off remote fonts, so that pages can only use fonts
         | you already have. Then you can just uninstall any fonts you
         | don't like or that you cannot read easily.
        
         | jml7c5 wrote:
         | I'm surprised you say that. Iosevka is quite beloved as a
         | monospace font. I use it for all my terminals, etc.
        
           | layer8 wrote:
           | Prose in monospace is harder to read regardless of the
           | specific font.
        
       | WalterBright wrote:
       | I gave up on environment variables long ago. Now my compilers
       | read a dmd.conf file that is in the same directory as the
       | compiler executable.
        
       | m3047 wrote:
       | ...and people have to find special ways to set them. I didn't
       | even know that pam_env existed until a recent security
       | vulnerability announcement. It's never come to my attention
       | before, I don't think I've ever seen it utilized. I've now made a
       | runbook item to disable it. It's a shame that "configuration"
       | includes undoing the fetishes and helpfulness [0][1] of others.
       | pam-config -d --env
       | 
       | [0] Crowded elevator atrium. Multiple elevators running. Elevator
       | wants to close, another one is coming (oh! I heard it "ding"!).
       | Somebody is holding the elevator which wants to depart and trying
       | to wave me in. Why doesn't somebody push them out?
       | 
       | [1] I'm at a stop sign. Some complete idiot is trying to turn
       | left onto the street I'm leaving and waving me to turn left in
       | front of them. Fuck no! I turn in front of you, somebody rearends
       | you, you fly forward into me: my fault! You should be able to
       | make this turn, if you can't go around the block! [2]
       | 
       | [2] I could go out of my way and turn right. Or I can just wait
       | and see what happens.
        
         | bandrami wrote:
         | If you need realtime scheduling pam_env was the only way to do
         | it until rtkit came out (and it still does it better than rtkit
         | does, annoyingly).
        
       | stackskipton wrote:
       | SRE/Sysadmin/DevOps/Whatever here, while blog didn't talk about
       | doing anything difficult but setting ENVVAR standards, I will
       | point out all replacements are just as frustrating especially
       | when talking about secrets.
       | 
       | Anything involving vaults where Application reaches out to
       | specific secret vault like Hashicorp Vault/OpenBao/Secrets
       | Manager quickly becomes massive vendor lock in where replacement
       | is very difficult due to library replacement and makes vault
       | uptime extremely important. This puts Ops in extremely difficult
       | place when it becomes time to upgrade or do maintenance.
       | 
       | Config files have problem of you have secrets, how do you get
       | them into config file since config files are generally kept in
       | public systems? Most of the time it's some form of either
       | "Template replacement by privileged system before handing it to
       | application" or "Entire Config File gets loaded into secret vault
       | and passed into application". Templating can be error prone and
       | loading entire config files into secret manager is frustrating as
       | well since someone could screw up the load.
       | 
       | Speaking of config files, since most systems are running
       | containers, and unless you are at Ops discipline company, these
       | config files are never in the right place, it becomes error prone
       | for Ops to screw up the mounting. Also, whatever format you use,
       | JSON/YAML/TOML is ripe for some weird config file bug to emerge
       | like Norway problem in YAML.
       | 
       | Getting secrets from Kubernetes Secrets API I've seen done but
       | lock in again. I'd strongly not recommend this approach unless
       | you are designing a Kubernetes operator or other such type
       | system.
       | 
       | I will say I've seen Subprocess thing bite people but I've seen
       | less and less subprocess generation these days. Most teams go
       | with message bus type system instead of sub processing since it's
       | more robust and allows independent scaling.
        
         | jppittma wrote:
         | How is the kubernetes secret API lock in? Genuinely wondering -
         | were you trying to use that deployment yaml for something other
         | than a kubernetes deployment? For most applications, you should
         | be mounting the secret on your application, then you can inject
         | it as either an environment variable or a json file that your
         | application reads in an environment agnostic way.
         | 
         | Then, on the backend, you can configure etcd to use whatever
         | KMS provider you like for encryption.
        
           | stackskipton wrote:
           | Because you can't run the container, even for development
           | outside Kubernetes.
           | 
           | Yes, you can mount Secrets as Volumes or Env Var in
           | Kubernetes which is fine but I'm not talking about "How you
           | get env var/secret" but "Methods of dealing with config."
        
             | Nilocshot wrote:
             | This is where I like things like Tilt. If you're deploying
             | to a k8s cluster, it's probably a good idea to do local dev
             | in as close to a similar environment as possible.
             | 
             | Bit more of an initial hurdle than "just run the docker
             | image"; however.
        
               | stackskipton wrote:
               | I've look at Tilt and it's another abstraction for
               | Kubernetes which rarely ends well at scale.
               | 
               | However, most of time, Devs don't need to develop on
               | Kubernetes since it's just Container Runtime and
               | Networking Layer they don't care about. They run
               | container, they connect to HTTP endpoint to talk to other
               | containers, they are happy. Details are left to us Ops
               | people.
        
             | jppittma wrote:
             | Yes you can? The container should be completely agnostic to
             | the fact that it's running in kubernetes. You can do config
             | the same way. Configmaps are mounted as regular files and
             | environment variables. The application doesn't care if the
             | configmap came from the cluster resource or a file your
             | created on your dev machine with dev credentials. You can
             | mount local files into the container yourself. It's docker
             | run -v "source:destination" I think.
        
               | stackskipton wrote:
               | _sigh_ I'm extremely competent Ops type and I know. If
               | you mount secrets as Volume or Env Var, that's Config
               | file or Env var from Application PoV. We are looking at
               | this from Application PoV.
               | 
               | I've seen Applications that do direct calls to Kubernetes
               | API and retrieve the secret from it. So they have custom
               | role with bindings and service account and Kubernetes
               | client libraries.
        
               | jppittma wrote:
               | If you're not developing k8s operators, you're calling
               | the api server directly, then complaining about lock in,
               | then that's a skill issue. If you're developing k8s
               | operators, then you should use a tool like kind for
               | integration tests and dependency injection for other
               | stuff and the concept of lock in doesn't make sense. You
               | can also deploy your helm chart directly to kind.
        
               | joshribakoff wrote:
               | One of you is talking about mapping a secret to an
               | environment variable and the other one of you is talking
               | about having the work load make an API call to retrieve
               | the secret. You're not even talking about the same thing.
        
             | cassianoleal wrote:
             | Don't use live system secrets and credentials when running
             | your application locally. Then you don't need to access the
             | same secrets.
             | 
             | Keep it simple and design your applications so they're
             | agnostic to that fact.
             | 
             | It's really not that hard, I've been doing this for at
             | least 6 or 7 years. A little bit of good engineering goes a
             | long way!
        
         | 1718627440 wrote:
         | You can have a command setting that is invoked to get the
         | string. This way you don't have vendor login, but also don't
         | need a separate template step.
        
           | stackskipton wrote:
           | You still need to present that to Application.
           | 
           | So Command Line leaks worse than Env Var.
           | 
           | Config file, see original post for problems.
           | 
           | Env Var, see blog for problems.
        
             | 1718627440 wrote:
             | No I meant a property in the application config.
             | 
             | For example mbsync/isync does this.
        
               | stackskipton wrote:
               | You have a config file, it needs to have secrets so
               | likely you are going to run some templating system where
               | you replace dbpassword: ${dbPassword} with password from
               | some secret system. Hopefuly you understand possible
               | issues with any templating system that could result in
               | replacement failures.
               | 
               | String manipulating is one of those "This is easy" until
               | it's not.
        
               | 1718627440 wrote:
               | Apparently I'm still unclear.
               | 
               | I don't mean to hardcode secrets into the config file
               | either. I was suggesting to put a command into the
               | configuration file that the application then calls to get
               | the secret. This way the secret is only ever passed over
               | a file descriptor between two processes.
        
               | sureglymop wrote:
               | I do something similar. I usually have a flag, something
               | like --password-file. It can only be used to specify a
               | file containing the secret and at startup the application
               | reads it.
        
               | 1718627440 wrote:
               | Yes, this is also possible, but which the approach I
               | stated, the secret can be generated by another program or
               | received from the network, it isn't just limited to a
               | file.
        
               | stackskipton wrote:
               | My initial Ops gut feeling says "This is as lock in and
               | error prone as Application Vault Libraries" but if Dev
               | wanted to propose this, I'd be willing to see it in real
               | operation.
        
               | 1718627440 wrote:
               | It isn't lock in, because all the application depends on
               | is that it gets a string it can pass to exec/the shell
               | and then reads all data from stdout until EOF as the
               | secret.
        
               | stackskipton wrote:
               | Sure, but let's say you have 5 secrets to get or maybe
               | the new vault CLI does not support just stdout printing
               | but prints JSON only.
               | 
               | I still think this is worse than config file/Env Vars.
        
               | 1718627440 wrote:
               | You can always pass echo "SECRET" as the command, so it
               | is a strict superset of the config file. Also programs
               | that tend to provide a command option also tend to
               | provide a simple string option.
        
             | sgarland wrote:
             | I think parent is referring to something like SOPS [0],
             | which can pass secrets via FIFO. That way, there's nothing
             | on disk, the pipe is cleared after first read, and
             | /proc/cmdline doesn't reveal anything.
             | 
             | 0: https://github.com/getsops/sops
        
         | nunez wrote:
         | +1 to all of this.
         | 
         | This is why I continue to use env vars and dotenv for
         | configuration. They are extremely simple, work well, and are
         | compatible with secrets managers and other secrets tooling.
         | 
         | Though lately I've been veering into sOps the last few years.
         | YAML is just so nice for expressing how an app should be
         | configured, and sops makes encrypting chunks of it so easy.
         | Dealing with GPG keys can be challenging though, which
         | Vault/OpenBao solve, but then lock-in becomes an issue (though
         | less so with OpenBao).
        
       | jhallenworld wrote:
       | Eh, they are just more command line arguments, ones that go on
       | the left side of the command instead of the right. I guess an
       | alternative is something the Windows registry, but I'm not seeing
       | that as a great improvement since it's less direct.
        
         | compiler-guy wrote:
         | Command-line arguments aren't passed to subprocesseses, can't
         | be inspected by arbitrary functions in the same process, and
         | don't leak memory if they are changed.
        
           | jhallenworld wrote:
           | Yes, I'm just highlighting that it's just as convenient to
           | change them as command line parameters on a per-invocation
           | basis. If there is a proposal for something else, it should
           | retain this capability. Config files and something like the
           | windows registry are much less convenient.
        
           | zokier wrote:
           | > can't be inspected by arbitrary functions in the same
           | process
           | 
           | You can not really rely on that. The initial stack layout is
           | well-defined at least on linux, so digging up argv is not
           | difficult. Or just open /proc/self/cmdline
        
       | jonny_eh wrote:
       | I can't recommend Varlock [1] enough. A great way to manage env
       | vars in a project. It lets you define which ones are necessary or
       | optional, their types, and where they should be fetched from.
       | 
       | [1] https://varlock.dev/
        
         | esafak wrote:
         | I use https://mise.jdx.dev/
        
       | peter_d_sherman wrote:
       | >"Wow, I really enjoyed writing this... ...and I hope it wasn't a
       | boring read."
       | 
       | No, it was very interesting actually!
       | 
       | An excellent deep-dive into the murky area of Unix/Linux
       | environment variables, how they are set, how they are passed,
       | what's really going on behind the scenes.
       | 
       | Basically a must-read for any present or future OS designer...
       | 
       | Observation (if I might!) -- environment variables are to
       | programs (especially chains of programs, parent processes, sub
       | processes and sub-sub processes, etc.) what parameters are to
       | functions -- and/or what command-line parameters are... they're
       | sort of like global variables that get passed around a lot...
       | 
       | They also can influence program behavior -- and thus the
       | determinism of a program -- and thus the determinism of a chain
       | of programs...
       | 
       | Phrased another way -- software that works on one developer's
       | machine might not work on another developer's machine and/or in a
       | production environment because one crucial environment variable
       | was either set or not set, or set to the wrong value...
       | 
       | (NixOS seems to understand this pretty well... that "hermetically
       | sealing" or closing or "performing a closure around" (that's
       | probably slightly the wrong language/terminology in "Nix-speak"
       | but bear with me!) a software environment, _including the
       | environment variables_ is the way to create deterministic
       | software builds that run on any machine... but here I 'm
       | digressing...)
       | 
       | But my main point: I complete agree with the article's author --
       | environment variables _are_ a legacy mess!
       | 
       | Although, if we think about it, environment variables (if we
       | broaden the definition) are a sub-pattern of _anything that
       | affects the state of an individual machine or runtime
       | environment_ -- in other words, things such as the Windows
       | Registry, at least the global aspects of it -- are also in the
       | same category.
       | 
       | Future OS's, when they offer environments for programs or chains
       | of programs to run -- should be completely containerized -- that
       | is, the view of the system -- what data/settings/environment
       | variables/registry variables/files/syscalls/global variables it
       | has access to -- should be completely determinable by the user,
       | completely logabble, completely transparent, and completely able
       | to be compared, one environment to another.
       | 
       | In this way, software that either a) fails to work at all b)
       | works in a non-deterministic way -- can be more easily
       | debugged/diagnosed/fixed (I'm looking at you, future AI's that
       | will assist humans in doing this!) -- then if all of that
       | information is in various different places, broken, and/or
       | opaque...
       | 
       | To reiterate:
       | 
       | >"Wow, I really enjoyed writing this... ...and I hope it wasn't a
       | boring read."
       | 
       | No, I _really enjoyed_ reading it(!), it 's a brilliant article,
       | and thank you for writing it! :-)
       | 
       | Upvoted and favorited!
        
       | alexpotato wrote:
       | To give an illustration of how bad this can get:
       | 
       | At a past firm, I was trying to debug how a particular ENV var
       | was getting set. I started out thinking it was something simple
       | like the user's .bashrc or equivalent.
       | 
       | I quickly realized that there were roughly 10 "layers" of env var
       | loadings happening where the first couple layers were:
       | 
       | - firm wide
       | 
       | - region
       | 
       | - business unit
       | 
       | - department
       | 
       | - team etc etc
       | 
       | I ended up having to turn on a bash debug flag so that I could
       | see exactly where the var was getting set in the layer stack.
        
         | jakub_g wrote:
         | Not sure if any other high level languages have it, but Node.js
         | has just added command line flags to trace precisely all env
         | var accesses and modifications:
         | 
         | https://nodejs.org/api/cli.html#--trace-env
         | 
         | Since you can set/unset/modify env vars in so many ways with
         | different APIs, this sounds super useful in complex debugging
         | scenarios.
        
         | esafak wrote:
         | "One namespace ought to be enough for anybody".
        
       | pluto_modadic wrote:
       | static secrets that you can even extract and set in a config file
       | (or store in a secrets manager) need to go. TPM secrets that you
       | can't extract, and public keys that you can pin without
       | considering them "secret", along with Oauth / IAM roles, are the
       | way to go.
        
       | quotemstr wrote:
       | Environment variables also suck due to having zero typo
       | existence. Write FOO_BAR instead of FOO_BARS? Silent failure. V1
       | of your package recognizes FOO_BAR and V2 changes it to FOO_BARS
       | (because now we can have more than one)? Silent failure.
        
         | sgarland wrote:
         | In a shell script, set -u. It'll terminate if you reference an
         | unset variable.
         | 
         | In other languages, check that the var you pulled in isn't
         | falsey before proceeding.
        
           | quotemstr wrote:
           | > In a shell script, set -u
           | 
           | Won't help. I'm _running a program_ want to configure it with
           | environment variables, not _writing a program_ that I expect
           | the user to configure for himself.
           | 
           | > falsey
           | 
           | There are more than two programming languages in the world.
        
             | sgarland wrote:
             | > There are more than two programming languages in the
             | world.
             | 
             | Great, then you know how to check it in your language of
             | choice.
        
       | sureglymop wrote:
       | On another note, does anyone know how this blog/website was
       | created? I am in general very curious about ssgs hence why I am
       | asking.
       | 
       | My guess would be either Jekyll or hand rolled, due to the url
       | structure.
        
       | trzy wrote:
       | Unix is a legacy mess. The sooner we rid ourselves of this
       | scourge, the better.
       | 
       | Posted from a productive Windows workstation.
        
         | scuff3d wrote:
         | You couldn't pay me enough to go back to doing dev work on
         | Windows.
        
         | taejavu wrote:
         | As if Windows isn't its own legacy mess..
        
       | nrvn wrote:
       | One of the worst things about Environment variables among others
       | discussed here is the implicit and opaque nature of them.
       | Majority of applications rely on them in the *nix world. Even if
       | more explicit and obvious ways of configuration files or remote
       | services (consul/etcd, et al.) and command line arguments are
       | supported env vars are traditionally supported as well.
       | 
       | But as mentioned in the article it is just a global hashmap that
       | can be cloned and extended for child processes. Maybe in 1979 it
       | was a good design decision. Today it sometimes hurts.
       | 
       | For example, kubernetes by default literally pollutes the
       | container's environment with so-called service links. And you
       | will have fun time debugging a broken application if any of those
       | "default" env vars conflict with the env vars that your app might
       | expect to see.
       | 
       | https://kubernetes.io/docs/tutorials/services/connect-applic...
       | 
       | They are ubiquitous and we are living in the world of neo-
       | conservatism in IT where legacy corner cuts are treated as a
       | standard and never challenged (hello /bin, /usr/bin, /lib,
       | /usr/lib)[0]
       | 
       | [0] - https://askubuntu.com/a/135679
        
         | oblio wrote:
         | Heh, you can put hjkl in that neo conservatism bucket. Vi hjkl
         | are the way they are because of a dumb terminal from 40+ years
         | ago, which had fewer units sold than the Nokia N9 smartphone.
        
       | agumonkey wrote:
       | OP: that wasn't a boring read :) thanks
        
       | hei-lima wrote:
       | Really, really interesting read. I use env vars daily, and it
       | never occurred to me how they actually work. It shows how many
       | things we take for granted have such interesting implementations.
       | :)
        
       | ahartmetz wrote:
       | My favorite environment variable trivium (that's the singular of
       | trivia) is that PS1 and a few others that "everyone" thinks of as
       | environment variables are not environment variables but _shell
       | variables_. PS1 doesn 't even show up in "env" output!
        
       ___________________________________________________________________
       (page generated 2025-10-13 23:01 UTC)