hashicorp / terraform

Terraform enables you to safely and predictably create, change, and improve infrastructure. It is a source-available tool that codifies APIs into declarative configuration files that can be shared amongst team members, treated as code, edited, reviewed, and versioned.
https://www.terraform.io
Other
42.83k stars 9.57k forks source link

Secret keys in Terraform remote state #22890

Open joe-boyce opened 5 years ago

joe-boyce commented 5 years ago

Terraform Version

Not specific to a version

Terraform Configuration Files

data "terraform_remote_state" "firewall" {
    backend = "gcs"
    config {
        bucket = " tf-state-bucket"
        prefix = "a/path"
        credentials = "${file("${var.provider_credentials}")}"
    }
}

Actual Behavior

This may have been discussed in depth before but I couldn't find a specific ticket covering the subject

When using a remote datasource in terraform we have noticed that the secret key being used to pull the data is dumped into the terraform remote state, is there any reason for this as it poses a security risk where by users who have pull access to read the remote datasource can steal secrets keys

Expected Behavior

Terraform shouldn't be storing secret keys in the remote state

Steps to Reproduce

  1. terraform show
  2. Example output:
    data.terraform_remote_state.firewall:
    id = 2019-09-23 15:18:26.321949 +0000 UTC
    mysql-master-tag = mysql-master-55e6362a5b17e26b
    mysql-slaves-tag = mysql-slaves-c4acdc3ff749cdc1
    backend = gcs
    config.# = 1
    config.2904908756.bucket = tf-state-bucket
    config.2904908756.credentials = {
    "type": "service_account",
    "project_id": "project-id",
    "private_key_id": "abcdefghijklmnopqrstuvwxyz123456789",
    "private_key": "-----BEGIN PRIVATE KEY-----\nabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyz\n-----END PRIVATE KEY-----\n",
    "client_email": "emailaddress",
    "client_id": "123456789101112",
    "auth_uri": "https://accounts.google.com/o/oauth2/auth",
    "token_uri": "https://accounts.google.com/o/oauth2/token",
    "auth_provider_x509_cert_url": "https://www.googleapis.com/oauth2/v1/certs",
    "client_x509_cert_url": "https://www.googleapis.com/robot/v1/metadata/x509/terraform%40email"
    }
    config.2904908756.prefix = a/path
    environment = default
    workspace = default
teamterraform commented 5 years ago

Hi @joe-boyce,

The Terraform backends and providers tend to offer arguments to override their default credentials-gathering behaviors in order to support more complex usage patterns, but the "normal" way to use backends and providers is to omit the credentials from the Terraform configuration altogether and instead allow the backend or provider to gather the credentials in a vendor-standard way from the environment.

For example, the GCS backend supports the documented mechanisms for configuring credentials for Google Cloud Platform, and that should be considered the default way to use this backend.

By keeping credentials out of the configuration we can ensure that the Terraform configuration only describes what Terraform is managing, and not where Terraform is running and who is running it.

The terraform_remote_state data source passes through all of the configuration arguments for the underlying backends, which does unfortunately mean it can accept this credentials argument, which will then be written to the state because Terraform saves data source results in order to use them in future plans. For this reason, you should not set credentials here and should instead allow the Google Cloud Platform credentials to be detected from the environment in the standard way. By doing so, the credentials will not be in the configuration and will thus not be saved in the state.

As a general rule, you should avoid using credentials and other secrets within the Terraform configuration itself, unless you have taken measures to treat the resulting state as a secret artifact. Other providers and backends have similar mechanisms for obtaining credentials automatically from the environment, and so the in-configuration arguments for providing credentials should always be considered a last resort for complex environments where ambient credentials are infeasible.

joe-boyce commented 5 years ago

Thanks this worked fine for google datasources, I omitted the credentials variable from the terraform_remote_state datasource and the API key was successfully used from the provider and not stored in the state file

On the AWS side we are using assume role for the provider, for example:

provider "aws" {
  version = ">= 1.60, < 2.0.0"
  region  = "${var.region}"
  profile = "${var.profile}"
  assume_role {
    role_arn     = "arn:aws:iam::123456789:role/${var.role}"
  }
}

This works fine but if I remove the accesskey & secretkey from the remote datasource it fails, for example:

data "terraform_remote_state" "iam" {
  backend = "s3"
  config {
    bucket     = "tf-state-bucket"
    key        = "path/to/iam/iam.tfstate"
    region     = "region"
  #  access_key = "${var.accesskey}"
  #  secret_key = "${var.secretkey}"
  }
}

Error is as folllows:

data.terraform_remote_state.iam: 1 error occurred:
    * data.terraform_remote_state.iam: data.terraform_remote_state.iam: error initializing backend: No valid credential sources found for AWS Provider.
    Please see https://terraform.io/docs/providers/aws/index.html for more information on
    providing credentials for the AWS Provider
mildwonkey commented 5 years ago

Hi @joe-boyce! Unfortunately your latest example is a known issue with the s3 backend. Here's one ticket, but I believe there are more open: https://github.com/hashicorp/terraform/issues/22354

There was a recent PR which addressed some, but not all, assume_role-related issues: https://github.com/hashicorp/terraform/pull/22994

joe-boyce commented 5 years ago

@mildwonkey do you know if this will make it into terraform v0.11.x as we haven't taken the leap onto 0.12.x as of yet, or will it be in the AWS provider?