terraform icon indicating copy to clipboard operation
terraform copied to clipboard

Adding a lockfile to guarantee Registry module versions are don't change

Open erickt opened this issue 8 years ago • 4 comments

Back in July 2017, @apparentlymart suggested adding a rubygems-esque lockfile that could allow terraform to verify that Registry modules don't change, but I couldn't find more details on it, so I filed this ticket.

I really appreciate the high quality modules in the Terraform Registry, but at the moment I'm pretty hesitant to actually use the functionality, especially in an automated fashion. As best as I can tell, Terraform allows users to pin modules to a particular version, like this:

module "vpc" {
  source = "terraform-aws-modules/vpc/aws"
  version = "1.14.0"
  ...
}

Unfortunately, it doesn't appear that Terraform captures a hash for dependencies like this, so there's risk that if the Registry is compromised, it could serve out malicious modules for version "1.14.0" that open up my infrastructure to attack. If we could have a lockfile that stores a cryptographic hash my external dependencies, like rubygems, it could protect us from attacks like this.

erickt avatar Jan 15 '18 17:01 erickt

Hi @erickt! Thanks for this feature request.

The discussion you referenced there unfortunately omitted some details about discussions we had internally during the development of module versioning. The most important thing for this discussion is that we decided to allow each module block to have its own separate version, even if two blocks reference the same source, which then allows the use of a blue/green strategy to transition from an older version to a newer version with the two versions briefly deployed together for continuity. An implication of that decision was moving away from the lock file idea, since that assumed a single global mapping from source string to version.

However, you make a good point here about another use-case for lock files: protection against malicious or unintended modification of an already-published module. We recently discussed internally a related idea of allowing validation of signed tags/releases in the git repository, which serves a similar purpose while also allowing you to know if a new version is considered trusted.

I agree that we should do something to address this use-case. We should at least record somehow a signature for each downloaded module which can be checked into your version control, but we'll need to do some prototyping to see what is the right UX/workflow here. For example, we'll need to make the right tradeoff between checking for integrity while still making it reasonably convenient to upgrade to a newer version.

In particular, we need to think about transitive dependencies: if module A depends on module B using a non-exact dependency, currently terraform init -upgrade will install the latest B that matches the given constraint, and there's no way for the calling module to override this. If we lock the entire tree on first terraform init we'll need a mechanism to manually "unlock" a particular module so that it's possible to upgrade to a newer version of B without modifying the source of A.


In the mean time, it should be possible to implement such an integrity check locally by hashing the .terraform/modules directory after terraform init is run. This will then cover the contents of all installed modules, throughout the module tree.

This assumes that exact version numbers are used throughout. If not, then running terraform init -upgrade (or terraform init in a fresh tree) may install newer modules that would then fail the signature.

apparentlymart avatar Jan 17 '18 01:01 apparentlymart

Lock files (like vendoring, git refs/submodules, and SemVer constraints) are a good tool for tying exact versions together to promote as a single unit but please also consider supporting artifact repositories (by having terraform init support a source from an artifact repository) for easier control over versions and variants across multiple deployment pipelines in an organization.

Using artifact repositories (Artifactory, Nexus, Pulp):

  • Outsources "locking" the known-working set to existing tools.
  • Allows layered repositories of different module lifecycles (infrastructure, application, base OS).
  • Allows deployment pipelines to lock or allow exceptions ("break glass") during promotions.
  • Can coordinate the versions of Terraform artifacts with other infrastructure tools (Chef, Puppet, Ansible, Packer, AMIs) and the application code (RPMs, .debs, MSIs, NuGets).
  • Provides known contexts (current repo version N and new repo version N-prime) to write blue-green, canary, and rolling deployment orchestration.

I wanted to mention repos as an alternative to or in addition to lock-files so I'll follow-up with a separate enhancement request for "Support artifact module builds and artifact repository sources" that better explains this different use-case since lock files, git refs, and current Enterprise don't support the "multiple pipelines" workflow well (that I'm aware of). An excellent description of the artifact workflow is in these diagrams and video.

kenmacleod avatar Mar 07 '18 22:03 kenmacleod

Hi @kenmacleod,

Supporting third-party artifact registries was a design goal for our registry-based module installer. The protocol used for module installation is designed to support multiple implementations, even though so far the team at HashiCorp has only implemented two instances of that protocol (the public Terraform Registry and the private registry feature in Terraform Enterprise). Over time we'd love to see other implementations emerge, hopefully including integrations into existing artifact repository software.

It seems to me that artifact registries and lock files are complementary, and so I would not expect any lockfile functionality added to change how Terraform interacts with module registries. Instead, it would serve as a means to record a specific set of modules that was used to apply a configuration so that future updates can use those exact versions or fail if that is not possible, as opposed to producing an unexpected plan and leaving the user to figure out what happened.

For those who prefer to centralize such "locking" in a central system, they would of course be free to ignore the lock file and just install "latest" (as decided by the artifact server) each time, or could choose to use the two mechanisms together as an integrity check.

apparentlymart avatar Mar 27 '18 16:03 apparentlymart

The risk of the Registry providing a reference to a malicious GitHub commit is a known issue that I documented in #29867.

In the meantime, I developed a module to address this issue. Feedback would be appreciated. https://registry.terraform.io/modules/Invicton-Labs/module-lock/null/latest 3

KyleKotowick avatar Jun 26 '24 19:06 KyleKotowick