terraform icon indicating copy to clipboard operation
terraform copied to clipboard

Add support for mTLS with remote custom registries

Open thomasgouveia opened this issue 3 years ago • 3 comments

Terraform Version

v1.3.3

Use Cases

When dealing with custom implementation of Terraform registries in companies, we can't currently use mTLS to ensure a complete secure connection between our Terraform run and our registry.

It would be great if we can provide, using the .terraformrc file or another configuration, custom certificates that will be used by Terraform to request the remote registry.

Attempted Solutions

As I can't provide custom certificates for my registry, I can't connect to it unless I disable the client authentication. It can be problematic if we have like me, a CLI that can interact with the remote server using mTLS. I wish to add the same behavior between my CLI and Terraform, to avoid dealing with multiple interfaces in my registry implementation.

Proposal

In .terraformrc, we can imagine have following configuration block :

registry "myregistry.company.com" {
     ca_cert = "/path/to/ca.crt"
     client_cert = "/path/to/client.crt"
     client_key = "/path/to/client.key"
} 

ca_cert will be the CA authority of the remote server. client_cert and client_key are used to authenticate on the remote server using mTLS.

When performing terraform init, if a module is located on the registry myregistry.company.com, custom certificates must be used to connect to it.

References

No response

thomasgouveia avatar Oct 28 '22 14:10 thomasgouveia

Thanks for sharing this idea, @thomasgouveia!

It is true that currently the only supported means of authenticating to a host is bearer tokens, which does make things rather asymmetrical: the server gets to authenticate itself using public key infrastructure while the client can only authenticate itself using a shared secret.

I can definitely see merit in what you are describing, and I think I'd only want to modify the design slightly to integrate better with existing architecture.

Terraform has an idea of "Terraform-native services" which are protocols native to Terraform which have some features in common to help minimize client-side setup work:

  • Services are provided at particular hostnames, which users then specify when using the service and Terraform automatically handles the details of which API endpoints to use, etc.
  • When multiple services are published under the same hostname, they all share the same authentication mechanism. This means that if all services you need are published under the same hostname then you only need to set up your client credentials once and they will be used for all services under that hostname.

Of course if you only intend to run a module registry then this abstraction isn't really adding much. But if you want to run multiple kinds of registries or any other protocol we might document in future then it's convenient to put them all under the same banner and amortize the client-side setup overhead.

All of that is to say that I think it would be valuable to integrate this into our existing mechanism for authenticating to Terraform-native service hosts, as a new kind of credential that can be set on a per-host basis.

Currently the CLI configuration can contain a block like this:

credentials "myregistry.example.com" {
  token = "..."
}

The above tells Terraform that it should use the given bearer token for all interactions with Terraform-native services on this host.

I would propose adding a new possible argument in this block for enabling TLS client auth and providing the settings needed to use it. This would be instead of the registry block you proposed, but have essentially the same meaning.

I think the are some remaining questions in the details of this:

  • Currently specifying a bearer token for a particular host makes Terraform use that token both for the initial discovery request and for any requests to service endpoints mentioned in the host's service discovery document. Would it be reasonable to say the same for TLS client certs?

    My initial instinct is that this behavior is actually even better for TLS client auth than for bearer tokens because the different services involved only need to share a root of trust (a CA cert) and don't need to all be aware of the same shared secret.

  • We offer "credentials helpers" as an extension point for dynamically issuing credentials, rather than hard-coding them on disk in a credentials block. A credentials helper essentially just dynamically generates the contents of a credentials block on request, and so it can use all of the same arguments Terraform would accept for static configuration.

    In this case though what is convenient for explicit configuration is not so good for dynamic: hard-coded blocks want to refer to cert and key files already on disk, but a dynamic credentials helper using e.g. the HashiCorp Vault PKI implementation would want to instead inline the cert and key material directly inside its response so that it only ever exists in memory on the client.

    That suggests the need for supporting both files and literal PEM content in the credentials for a host, so that both approaches are available to suit different situations.

  • We have a terraform login command for interactively issuing credentials, currently using OAuth. Certificate issuing is typically a different workflow than OAuth, where the client generates its own private key and sends a signed request to the CA. Would it be useful and valuable to support interactively issuing TLS client certs via this command, or are the requirements here too different to fit well into the current design of this command?

We aren't currently working on stuff in this area so I expect we won't be able to act on this immediately, but my goal with the above was to capture some context that might be useful to a future person who wants to work on a finalized design to achieve this. Because this is a security-related feature we would need to produce a design doc and have our product security team review it before proceeding to implementation.

Thanks again!

apparentlymart avatar Oct 28 '22 16:10 apparentlymart

Hello @apparentlymart , and thank you for your detailed explanation !

I'm really interested in your proposal, I think it is better to extends the existing credentials block instead of creating a new one.

For sure, we can imagine like you said, having a compatibility for inline PEM and certificates files. It will be really helpful in a company for example if we can use the Vault PKI engine to generate the certificates.

For issuing the certificates on the client side, I'm not sure of the workflow if there is no Vault PKI engine or something which provides an API to generate them. Maybe you have an idea for that?

I think if mTLS is enabled on the registry, the flow to check if the user is authenticated is significantly better, because if mTLS connection is established, it probably means in most of cases that the client is correctly authenticated on the server.

I'm really grateful that you consider my idea. I hope to see this in Terraform in future releases. If you need more details, don't hesitate to ping me !

thomasgouveia avatar Oct 31 '22 16:10 thomasgouveia

Hi, what is the status here? Is there some workaround available?

Let me explain the situation, as I can image more people come here with similar issues:

We currently use a private, self-hosted GitLab and access it from a GL Runner in some cloud. Connecting to GitLab requires Client Certificates. Hence, authenticating to the Terraform Backend (GitLab) requires Client Certificates. We use the ENV vars TF_HTTP_CLIENT_CERTIFICATE_PEM etc (see https://github.com/hashicorp/terraform/pull/32580/files), but these seem to apply only to the HTTP Backend, not the module registry.

Hence, we effectively cannot use the GitLab TF Registry, as long as GitLab is secured via mTLS and not openly exposed on the Internet.. Terraform simply does not send along the Client Certs when requesting the module registry.

Is there any known workaround or general advice from Hashicorp?

Thanks and regards!

0snap avatar Jun 18 '24 09:06 0snap