terraform-aws-modules / terraform-aws-iam

Terraform module to create AWS IAM resources 🇺🇦
https://registry.terraform.io/modules/terraform-aws-modules/iam/aws
Apache License 2.0
787 stars 996 forks source link

parsing given PGP key: openpgp: invalid data: tag byte does not have MSB set #430

Closed SuperCoolAlan closed 9 months ago

SuperCoolAlan commented 12 months ago

Description

When using the iam-user module, importing a base64-encoded pgp_key generated with gpg gives the following error:

╷
│ Error: creating IAM User Login Profile for "vasya.pupkin": encrypting Password: parsing given PGP key: openpgp: invalid data: tag byte does not have MSB set
│ 
│   with module.iam_user.aws_iam_user_login_profile.this[0],
│   on .terraform/modules/iam_user/modules/iam-user/main.tf line 12, in resource "aws_iam_user_login_profile" "this":
│   12: resource "aws_iam_user_login_profile" "this" {
│ 
╵
╷
│ Error: creating IAM Access Key (vasya.pupkin): encrypting IAM Access Key Secret: parsing given PGP key: openpgp: invalid data: tag byte does not have MSB set
│ 
│   with module.iam_user.aws_iam_access_key.this[0],
│   on .terraform/modules/iam_user/modules/iam-user/main.tf line 26, in resource "aws_iam_access_key" "this":
│   26: resource "aws_iam_access_key" "this" {

I have attempted to remove gpg's --armor flag, attempted to add the --no-default-keyring flag to gpg, attempted to base64 encode only the contents between the PGP key's labels (like "-----BEGIN PGP PUBLIC KEY BLOCK-----"), and attempted to encode the entire key including the labels. I receive parsing errors when not using the --armor flag and labels.

Versions

Create your own PGP key with gpg

#!/usr/bin/env bash
set -e

SCRIPT_DIR=$(readlink -f $(dirname $0))

PGP_PUBKEY_DIR=$SCRIPT_DIR/pgp_pubkey.gpg

# check if a key already exists in the keychain
# if it does exist, exit
prevKey=$(sudo gpg -k)
if [ ! -z "$prevKey" ]; then
    echo "A PGP key already exists!"
    echo $prevKey
    echo "exiting gracefully..."
    exit
fi

# create a new key
newKeyId=$(sudo gpg --gen-key | head -2 | tail -1 | sed 's/^[ \t]*//')
echo "A new PGP key has been generated with ID: $newKeyId"

# export the public key for use with Terraform
sudo gpg --armor --output $PGP_PUBKEY_DIR --export
echo "Your public PGP key was exported to $PGP_PUBKEY_DIR"

client_keys.tf

data "external" "client_pgp_pubkey" {
  program = ["/bin/bash", "-c", "echo '{\"data\":' ; ((cat ./pgp_pubkey.gpg | base64) | jq --raw-input --slurp); echo '}'"]
}

locals {
  client_pgp_pubkey = [for i in split("\n", data.external.client_pgp_pubkey.result.data) : i if i != ""]
}

output "client_pgp_pubkey" {
  value = local.client_pgp_pubkey
}
module "iam_user" {
  source = "terraform-aws-modules/iam/aws//modules/iam-user"

  name                    = "vasya.pupkin"
  force_destroy           = true
  create_iam_access_key   = true
  password_reset_required = false

  pgp_key = local.client_pgp_pubkey[0]

}

outputs.tf

output "iam_user_name" {
  description = "The user's name"
  value       = module.iam_user.iam_user_name
}

output "iam_user_arn" {
  description = "The ARN assigned by AWS for this user"
  value       = module.iam_user.iam_user_arn
}

output "iam_user_unique_id" {
  description = "The unique ID assigned by AWS"
  value       = module.iam_user.iam_user_unique_id
}

output "iam_user_login_profile_key_fingerprint" {
  description = "The fingerprint of the PGP key used to encrypt the password"
  value       = module.iam_user.iam_user_login_profile_key_fingerprint
}

output "iam_user_login_profile_encrypted_password" {
  description = "The encrypted password, base64 encoded"
  value       = module.iam_user.iam_user_login_profile_encrypted_password
}

output "iam_user_login_profile_password" {
  description = "The user password"
  value       = module.iam_user.iam_user_login_profile_password
  sensitive   = true
}

output "iam_access_key_id" {
  description = "The access key ID"
  value       = module.iam_user.iam_access_key_id
}

output "iam_access_key_key_fingerprint" {
  description = "The fingerprint of the PGP key used to encrypt the secret"
  value       = module.iam_user.iam_access_key_key_fingerprint
}

output "iam_access_key_encrypted_secret" {
  description = "The encrypted secret, base64 encoded"
  value       = module.iam_user.iam_access_key_encrypted_secret
}

output "iam_access_key_secret" {
  description = "The access key secret"
  value       = module.iam_user.iam_access_key_secret
  sensitive   = true
}

output "iam_access_key_ses_smtp_password_v4" {
  description = "The secret access key converted into an SES SMTP password"
  value       = module.iam_user.iam_access_key_ses_smtp_password_v4
  sensitive   = true
}

output "iam_access_key_status" {
  description = "Active or Inactive. Keys are initially active, but can be made inactive by other means."
  value       = module.iam_user.iam_access_key_status
}

output "pgp_key" {
  description = "PGP key used to encrypt sensitive data for this user (if empty - secrets are not encrypted)"
  value       = module.iam_user.pgp_key
}

output "keybase_password_decrypt_command" {
  description = "Decrypt user password command"
  value       = module.iam_user.keybase_password_decrypt_command
}

output "keybase_password_pgp_message" {
  description = "Encrypted password"
  value       = module.iam_user.keybase_password_pgp_message
}

output "keybase_secret_key_decrypt_command" {
  description = "Decrypt access secret key command"
  value       = module.iam_user.keybase_secret_key_decrypt_command
}

output "keybase_secret_key_pgp_message" {
  description = "Encrypted access secret key"
  value       = module.iam_user.keybase_secret_key_pgp_message
}

output "policy_arns" {
  description = "The list of ARNs of policies directly assigned to the IAM user"
  value       = module.iam_user.policy_arns

Steps to reproduce the behavior:

Expected behavior

When importing a self-generated PGP key, do not return error and encrypt secrets like when using keybase.

Actual behavior

PGP format is missing data

Terminal Output Screenshot(s)

Additional context

wonko commented 11 months ago

It seems you're inputting the key in the wrong format. This is what I did to have this working:

tf code:

provider "aws" {
  profile = "..."
  region  = "eu-central-1"
}

locals {
  client_pgp_pubkey = filebase64("${path.module}/gpg_public.raw")
}

module "iam_user" {
  source = "terraform-aws-modules/iam/aws//modules/iam-user"

  name                    = "issue.430"
  force_destroy           = true
  create_iam_access_key   = true
  password_reset_required = false

  pgp_key = local.client_pgp_pubkey
}

Key creation:

$ export PGP_PUBKEY_DIR=`pwd`
$ gpg --gen-key
... (fill in name, email, ...)
$ gpg --export -o gpg_public.raw 

TF output

➜ terraform apply                    

Terraform used the selected providers to generate the following execution plan.
Resource actions are indicated with the following symbols:
  + create

Terraform will perform the following actions:

  # module.iam_user.aws_iam_access_key.this[0] will be created
  + resource "aws_iam_access_key" "this" {
      + create_date                    = (known after apply)
      + encrypted_secret               = (known after apply)
      + encrypted_ses_smtp_password_v4 = (known after apply)
      + id                             = (known after apply)
      + key_fingerprint                = (known after apply)
      + pgp_key                        = "mDMEZVR6DBY...[REDACTED]...qHAQ=="
      + secret                         = (sensitive value)
      + ses_smtp_password_v4           = (sensitive value)
      + status                         = "Active"
      + user                           = "issue.430"
    }

  # module.iam_user.aws_iam_user.this[0] will be created
  + resource "aws_iam_user" "this" {
      + arn           = (known after apply)
      + force_destroy = true
      + id            = (known after apply)
      + name          = "issue.430"
      + path          = "/"
      + tags_all      = (known after apply)
      + unique_id     = (known after apply)
    }

  # module.iam_user.aws_iam_user_login_profile.this[0] will be created
  + resource "aws_iam_user_login_profile" "this" {
      + encrypted_password      = (known after apply)
      + id                      = (known after apply)
      + key_fingerprint         = (known after apply)
      + password                = (known after apply)
      + password_length         = 20
      + password_reset_required = false
      + pgp_key                 = "mDMEZVR6DBY...[REDACTED]...qHAQ=="
      + user                    = "issue.430"
    }

Plan: 3 to add, 0 to change, 0 to destroy.

Do you want to perform these actions?
  Terraform will perform the actions described above.
  Only 'yes' will be accepted to approve.

  Enter a value: yes

module.iam_user.aws_iam_user.this[0]: Creating...
module.iam_user.aws_iam_user.this[0]: Creation complete after 1s [id=issue.430]
module.iam_user.aws_iam_access_key.this[0]: Creating...
module.iam_user.aws_iam_user_login_profile.this[0]: Creating...
module.iam_user.aws_iam_user_login_profile.this[0]: Creation complete after 0s [id=issue.430]
module.iam_user.aws_iam_access_key.this[0]: Creation complete after 0s [id=AKIAYK6MP27KUKOV4VMJ]

The whole jumbling of the key through a JSON format and back seems unneeded. filebase64 is exactly what you need. The documentation for iam_access_key (which is used in the module) at https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/iam_access_key#pgp_key also reflects the format you need to feed to the pgp_key attribute.

github-actions[bot] commented 10 months ago

This issue has been automatically marked as stale because it has been open 30 days with no activity. Remove stale label or comment or this issue will be closed in 10 days

github-actions[bot] commented 9 months ago

This issue was automatically closed because of stale in 10 days

SuperCoolAlan commented 9 months ago

Thanks for the fix! You are right - my redirection to JSON was incorrect.

github-actions[bot] commented 8 months ago

I'm going to lock this issue because it has been closed for 30 days ⏳. This helps our maintainers find and focus on the active issues. If you have found a problem that seems similar to this, please open a new issue and complete the issue template so we can capture all the details necessary to investigate further.