Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Introduce dependency lockfile #1634

Open
wata727 opened this issue Dec 29, 2022 · 1 comment
Open

Introduce dependency lockfile #1634

wata727 opened this issue Dec 29, 2022 · 1 comment
Labels
enhancement needs-design Detailed design is required for implementation

Comments

@wata727
Copy link
Member

wata727 commented Dec 29, 2022

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:

plugin "terraform" {
    enabled = true
    version = "0.2.2"
    source  = "github.com/terraform-linters/tflint-ruleset-terraform"
}

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:

plugin "github.com/terraform-linters/tflint-ruleset-terraform" {
  version = "0.2.2"
  hashes = [
    "h1:FJwsuowaG5CIdZ0WQyFZH9r6kIJeRKts9+GcRsTz1+Y=",
    "h1:c/ntSXrDYM1mUir2KufijYebPcwKqS9CRGd3duDSGfY=",
    "h1:yre4Ph76g9H84MbuhZ2z5MuldjSA4FsrX6538O7PCcY=",
  ]
}

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:

  • 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
  • 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.

References

@wyardley
Copy link

wyardley commented Oct 15, 2024

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.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
enhancement needs-design Detailed design is required for implementation
Development

No branches or pull requests

2 participants