Live data from Hacker News

The Twelve-Factor App (2011)

12factor.net

51–60 of 106 posts

Re: The Twelve-Factor App (2011)

#51

Earlier quoted context omitted.

Seconded, but porque no los dos? I tend to put my applications' env vars in `/etc/environment`.

It’s a good question and idea. I’ve been trending towards treating secrets and non-secrets differently. For non-secrets, I tend to have a config discovery mechanism that lets me read config from files, then from env, then from cli args as final fallback. This is mostly for ergonomics. I prefer to use files for deployed processes, and env vars or cli params when debugging or working interactively. Env vars are handy f…

For what it's worth at $dayjob (public company, 1B+ revenue) we do the exact same, although either I misunderstood you or we have the exact opposite priority: args always win, if they're not defined then env wins, then config file, then default config value in code which sometimes is an actual value, and sometimes it defaults to throwing an exception and never passing a readiness check, depending on the behavior of the config in question. This last one is mostly a relic of the past and we believe it's better to blow up if there's no default configuration value in the chart in all cases; it also makes it way easier not to have to chase defaults through the code.

We use these for running locally and debugging as you say, but come deployment time we ship all applications as helm charts with default config values, store overrides and static secrets (with SOPS) in a different, company-global repo, and use kubernetes secrets to mount a tmpfs volume to pass them in to the application. There's no way to pass args or env there.

Re: The Twelve-Factor App (2011)

#52
I really dislike 12FA. It's meaningless. The name sounds neat, but it is undistinguishable from "some guy's twelve preferences".

Of course advocating for "some guy's twelve preferences" would never be considered serious argumentation in an engineering context. Yet we do for 12FA.

I might agree with this or that point, but taking the package as a whole just leads to weaponization and cargo-culting.

Re: The Twelve-Factor App (2011)

#53
post #52

I really dislike 12FA. It's meaningless. The name sounds neat, but it is undistinguishable from "some guy's twelve preferences". Of course advocating for "some guy's twelve preferences" would never be considered serious argumentation in an engineering context. Yet we do for 12FA. I might agree with this or that point, but taking the package as a whole just leads to weaponization and cargo-culting.

I disagree. It perhaps started as someone's preference, but the fact that it got so widely known and shared made it something more. Same goes for the Joel Test.

And it's not about cargo-culting, it's value lies mostly in having a set of things you can show your company: "this is industry standard, we're far from there and should work on that".

Not everything in these lists needs to be done that way. But they're still nice pointers for where to move towards (or beyond).

Re: The Twelve-Factor App (2011)

#54

One thing that I think could be updated in the 12F approach is the use of env vars for config, especially secrets. I’ve generally found it much better to mount these into the filesystem and read from there. It helps with, for example, secret rotation for long running processes. Relying on process restarts can be ugly in some setups, especially if startup time is expensive.

Check out the Secretless Broker at https://secretless.io . It's a cool open source project that allows applications to not need to know secrets which adheres to 12-factor app guidelines.

I’ll have to dig into it to see how it compares, but https://spiffe.io/ is what I look to in this area.

Not having long lived secrets is the ultimate destination, but we all live with the legacy around us.

Re: The Twelve-Factor App (2011)

#55

One thing that I think could be updated in the 12F approach is the use of env vars for config, especially secrets. I’ve generally found it much better to mount these into the filesystem and read from there. It helps with, for example, secret rotation for long running processes. Relying on process restarts can be ugly in some setups, especially if startup time is expensive.

The best approach I've seen is to have the secrets in a vault and to keep the vault URL in an env var (so different vaults can be used for dev and prod, for example). The secrets location within the vault is held in a variable defined in an application config file which is checked in to source control. At runtime, a piece of code consults the variable and queries the vault using the provided location and then dynamically configures the db connection with the returned credentials. The credentials are dumped from memory once the transaction completes.

(I should also point out that this is in a batch ETL system.)

The nice thing about this approach is that it is virtually impossible for secrets to end up in source control. The risk is that the vault becomes a major single point of failure.

Re: The Twelve-Factor App (2011)

#56
post #55

One thing that I think could be updated in the 12F approach is the use of env vars for config, especially secrets. I’ve generally found it much better to mount these into the filesystem and read from there. It helps with, for example, secret rotation for long running processes. Relying on process restarts can be ugly in some setups, especially if startup time is expensive.

The best approach I've seen is to have the secrets in a vault and to keep the vault URL in an env var (so different vaults can be used for dev and prod, for example). The secrets location within the vault is held in a variable defined in an application config file which is checked in to source control. At runtime, a piece of code consults the variable and queries the vault using the provided location and then dynamic…

It also makes cycling credentials easy. All you have to do is update the values in the vault. No need to hunt down all those places where you set environment variables.

I think you don’t even need to redeploy any long-running tasks. If they have a connection open, it should continue working, and if they don’t, they should consult the vault when they open one and get the new credentials from it (this may be different for different kinds of credentials)

There will be a race condition between creating credentials and storing them in the vault, but even that can be avoided:

  - create new credentials with same rights as the old ones
  - update vault
  - delete old credentials
Another race where credentials get changed between reading them from the vault and using them can’t be avoided, but if you wait a few minutes before you delete the old credentials, it’s extremely unlikely you’ll hit it.

Re: The Twelve-Factor App (2011)

#57
post #43
post #41

Earlier quoted context omitted.

Correct! This is why I explicitly said the following in the blog post I linked above: > Once the application hits “readiness” status (as determined by health endpoints or the load balancer), the secrets volume should be unmounted and made inaccessible.

Then I'll just pull it from the processes memory directly. It's security through obscurity at best.

Just an arbitrary file read vulnerability away at /proc/$pid/mem

Re: The Twelve-Factor App (2011)

#58
I really like 12 Factor Apps, the set of ideas and approaches has allowed me to make a few personal projects more easy to setup and run.

Additionally, i worked on the webpage and some components for Apturi Covid (Latvia's COVID contact tracing app, perhaps a bit less relevant now) which was also really easy to hand over to Ops to run thanks to the simple configuration: https://apturicovid.lv/#en

That said, at work there are still some people who don't want to use those approaches, their argumentation being along the lines of:

  - config: "we don't like long lists of environment variables, files are easier to read" (ignoring that those don't play nicely with containers)
  - config: "files let you group things more easily (processes.properties, datasource.properties, urls.properties)" (ignoring discoverability issues)
  - backing services: "ehh, there's nothing wrong with hardcoding at least parts of a path in the app" (disregarding that context paths can change)
  - build, release, run: "just put some default config in the app directory for running locally, bundle the front end and back end for ease of deployment" (ignoring that this bundling makes things more risky in regards to accidentally committing/shipping the wrong stuff, and bundling components slows everything down, can't build FE/BE in parallel)
  - processes: "how do i restart Tomcat inside of the container?" (treating containers as VMs, though 12FA aren't necessarily bound to containers per se)
  - concurrency: (the entire system is built in a way that is only compatible with a single instance, local filesystem used for stuff etc.)
  - disposability: (the older apps take minutes to start up due to being large monoliths)
  - logs: "but files are easier to work with" (they're only easier if you don't have proper log shipping infra in place)
What's my point with all of this? Well, for starters, if your apps are bad (large clunky monoliths), then it's not like 12 Factor App principles will make things that much better for you, since the actual implementation might be different due to previous assumptions that were made in the app design.

And even if they could, then you still have other people with conflicting opinions or simply views of cloud native and container based applications that will not have you easily running or scaling these apps anytime soon.

It's not like every system needs to be structured like that, but sometimes i wonder whether it would be easier to just work on new projects only and not bother modernizing the older ones. For all i (perhaps should) care about, those older systems might as well run with their config and log files on the file system, inside of Tomcat instances with JDK version and config drift, no scalability and manual restarts after crashes when things inevitably break, as well as eventual disk space issues - just with someone who feels comfortable spending their time that way behind the wheel, not me.

A bit like the blog post "Green Vs. Brown Programming Languages" which talked about most of the dreaded programming languages being old and most of the loved ones being new: https://earthly.dev/blog/brown-green-language/ Perhaps that's simply due to there not being any old legacy codebases to maintain in the newer languages, ergo them being liked.

Re: The Twelve-Factor App (2011)

#59
post #24

One thing that I think could be updated in the 12F approach is the use of env vars for config, especially secrets. I’ve generally found it much better to mount these into the filesystem and read from there. It helps with, for example, secret rotation for long running processes. Relying on process restarts can be ugly in some setups, especially if startup time is expensive.

I agree completely. Storing secrets in environment variables is wrong: https://blog.forcesunseen.com/stop-storing-secrets-in-enviro... Even unsetting environment variables leaves them in /proc/self/environ. There isn't a thread-safe way to unset environment variables in POSIX, so even if you `unset TOP_SECRET` from within a program the contents of /proc/self/environ will remain unchanged and available.

How do you actually unmount the volume with kubernetes once the secrets are read and the process is ready? I've never heard of a feature like that.

Re: The Twelve-Factor App (2011)

#60
post #55

One thing that I think could be updated in the 12F approach is the use of env vars for config, especially secrets. I’ve generally found it much better to mount these into the filesystem and read from there. It helps with, for example, secret rotation for long running processes. Relying on process restarts can be ugly in some setups, especially if startup time is expensive.

The best approach I've seen is to have the secrets in a vault and to keep the vault URL in an env var (so different vaults can be used for dev and prod, for example). The secrets location within the vault is held in a variable defined in an application config file which is checked in to source control. At runtime, a piece of code consults the variable and queries the vault using the provided location and then dynamic…

This is the way!
Post reply on HN