You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
TFLint has a mechanism to install plugins, so you can easily extend rules by third-party plugins. Configure the plugins as follows and install them with tflint --init:
Version constraints are not available and only a single version can be set. Although this seems to prevent the installation of unintended versions, it is not quite complete. Repojacking can allow an attacker to trick a user into installing a malicious plugin. The environment running TFLint is likely to contain sensitive info such as access tokens, so preventing such attacks is important.
Verifying a signature mitigates the impacts of this problem. The installation fails because the attacker does not know the plugin developer's signing key. Fortunately, plugins under the "terraform-linters" organization have their signatures automatically verified, so you're less likely to run into this problem.
However, things change when we adopt keyless signing. Keyless signing verifies the ID token issued by GitHub Actions instead of the plugin developer's private key. In other words, if the attacker succeeds in repojacking, they can issue valid ID tokens.
Hashes are the sha256 of the plugin binary on all platforms. When the lockfile entry exists, installed plugins are tested that their hash matches the hashes in the lockfile. If they don't match, the installation will fail, preventing you from installing anything other than the first trusted plugin. If the entry does not exist in the lockfile, TFLint appends an entry with hashes to the lockfile.
A notable difference is that --upgrade flag is not required. tflint --init can always update the lockfile because TFLint does not support version constraints.
There are some considerations for implementation:
File name and location
The name and location of the lockfile are not obvious in that case, as you can specify a different file with the --config flag. Perhaps it's worth looking at the behavior of similar tools. For example, Bundler can specify any path with the --gemfile flag.
Type of hash to record
In the example above, only the h1: hash is recorded, but there is room for discussion on whether other hashes should be recorded. For example, Terraform records zh: hashes as well.
Given the discussion above, zh: might be sufficient for TFLint. On the other hand, h1: is better in terms of flexibility. If we record h1: hashes, will need checksums.txt with h1: hashes available for all platforms (currently only zh: hashes are included).
Compatibility with automated update workflows
If using an automated workflow such as Renovate, the lockfile should contain hashes for all platforms, as tflint --init may run on different platforms. The key is to avoid unnecessary lockfile diffs in any case. Terraform only records the h1: hash of the platform it runs on, which should be avoided.
We may need to provide a mechanism to easily update the lockfile from Renovate.
Application to plugin.SecureConfig
SecureConfig allows go-plugin to verify the checksum of the binary before executing the plugin. The hashes in the lockfile may be available for this checksum. But in that case, we want the h1: hash instead of the zh:. See also Configure go-plugin's SecureConfig tflint-plugin-sdk#192.
May want to send a preemptive heads up to the Renovate folks if / when this is implemented, so that they can try to implement lockfile updating in the tflint plugin manager (edit: I see it's noted above)
For terraform, at least, it's implemented in code vs. using the actual binary, but in theory, tflint may not have some of the limitations that are behind that
--upgrade flag is not required. tflint --init can always update the lockfile because TFLint does not support version constraints.
I wonder if there's a different name that should be used in this case, since, as you note, the version is always pinned anyway? I normally think of the lockfile (a concept that mostly comes from package managers) as "locking" the specific version where there's a fuzzy version spec vs. "locking" to a particular hash / signing key, though I guess both meanings could theoretically make sense.
Introduction
TFLint has a mechanism to install plugins, so you can easily extend rules by third-party plugins. Configure the plugins as follows and install them with
tflint --init
:Version constraints are not available and only a single version can be set. Although this seems to prevent the installation of unintended versions, it is not quite complete. Repojacking can allow an attacker to trick a user into installing a malicious plugin. The environment running TFLint is likely to contain sensitive info such as access tokens, so preventing such attacks is important.
Verifying a signature mitigates the impacts of this problem. The installation fails because the attacker does not know the plugin developer's signing key. Fortunately, plugins under the "terraform-linters" organization have their signatures automatically verified, so you're less likely to run into this problem.
However, things change when we adopt keyless signing. Keyless signing verifies the ID token issued by GitHub Actions instead of the plugin developer's private key. In other words, if the attacker succeeds in repojacking, they can issue valid ID tokens.
Proposal
To mitigate this problem, we introduce dependency lockfile just like Terraform.
https://developer.hashicorp.com/terraform/language/files/dependency-lock
Running
tflint --init
will create a.tflint.lock.hcl
file like this:Hashes are the sha256 of the plugin binary on all platforms. When the lockfile entry exists, installed plugins are tested that their hash matches the
hashes
in the lockfile. If they don't match, the installation will fail, preventing you from installing anything other than the first trusted plugin. If the entry does not exist in the lockfile, TFLint appends an entry with hashes to the lockfile.These behaviors are almost the same as Terraform, so implement them with reference to the following documentation.
https://developer.hashicorp.com/terraform/language/files/dependency-lock#dependency-installation-behavior
https://developer.hashicorp.com/terraform/language/files/dependency-lock#understanding-lock-file-changes
A notable difference is that
--upgrade
flag is not required.tflint --init
can always update the lockfile because TFLint does not support version constraints.There are some considerations for implementation:
--config
flag. Perhaps it's worth looking at the behavior of similar tools. For example, Bundler can specify any path with the--gemfile
flag.h1:
hash is recorded, but there is room for discussion on whether other hashes should be recorded. For example, Terraform recordszh:
hashes as well.zh:
andh1:
because it neededh1:
to introduce Local Filesystem Mirrors. See also Feature request: Generate .terraform.lock.hcl including zh and h1 hash values for given platforms from required_providers block without downloading providers and modules hashicorp/terraform#27264 (comment) for details.zh:
might be sufficient for TFLint. On the other hand,h1:
is better in terms of flexibility. If we recordh1:
hashes, will needchecksums.txt
withh1:
hashes available for all platforms (currently onlyzh:
hashes are included).tflint --init
may run on different platforms. The key is to avoid unnecessary lockfile diffs in any case. Terraform only records theh1:
hash of the platform it runs on, which should be avoided.plugin.SecureConfig
hashes
in the lockfile may be available for this checksum. But in that case, we want theh1:
hash instead of thezh:
. See also Configure go-plugin'sSecureConfig
tflint-plugin-sdk#192.References
SecureConfig
tflint-plugin-sdk#192The text was updated successfully, but these errors were encountered: