Let Infrastructure Provisioners Create EC2 Instances Without Giving Away SSH Key Pair Access

Unless you’re a startup or a small business, it’s very likely that the people who create your AWS infrastructure are not the ones who work with it. For example, you might have an infrastructure team who is tasked with creating EC2 instances, while only a dev team should be able to login to the instances & deploy apps.

Assuming you’re using some Infra-as-Code solution (CloudFormation or Terraform), the normal way to do this would be for the infra team to manually create SSH key pairs from the EC2 console & reference them in the CloudFormation or Terraform scripts.

But this means that the infra team must download the private key when it’s created & share it with the devs in some secure manner. But the infra team shouldn’t have the private key at all. We don’t want them to SSH into the instances. Also, wouldn’t it be great if we could store all our private keys in AWS-provided secure storage, instead of elsewhere outside AWS?

This article describes one way to do this (in Terraform). The idea is very simple: The Terraform script creates the key pair & saves it to AWS Secrets Manager right away. This way, the infra team never sees the private key & all you have to do is restrict permissions on the secret in Secrets Manager to the dev team so they can download the key & SSH into their instances!

As it turns out, the folks at rhythmictech have created a Terraform module to do all of this one go. Check it out here: secretsmanager-keypair. Using this module, the entire Terraform script can be wrapped up in just a few lines:

terraform {
  required_providers {
    aws = {
      source = "hashicorp/aws"
      version = "3.36.0"

provider "aws" {
  region = "ap-south-1"
  access_key = "..."
  secret_key = "..."

module "secretsmanager-keypair" {
  source  = "rhythmictech/secretsmanager-keypair/aws"
  version = "0.0.3"
  name_prefix = "my-key-pair-"

Now like any other Terraform script, just init, plan & apply:

$ terraform apply

An execution plan has been generated and is shown below.
Resource actions are indicated with the following symbols:
  + create

Terraform will perform the following actions:

  # module.secretsmanager-keypair.aws_key_pair.keypair will be created
  + resource "aws_key_pair" "keypair" {
      + arn             = (known after apply)
      + fingerprint     = (known after apply)
      + id              = (known after apply)
      + key_name        = (known after apply)
      + key_name_prefix = "my-key-pair-"
      + key_pair_id     = (known after apply)
      + public_key      = (known after apply)

  # module.secretsmanager-keypair.aws_secretsmanager_secret.secret_key will be created
  + resource "aws_secretsmanager_secret" "secret_key" {
      + arn                     = (known after apply)
      + description             = "ssh key"
      + id                      = (known after apply)
      + name                    = (known after apply)
      + name_prefix             = "my-key-pair-"
      + policy                  = (known after apply)
      + recovery_window_in_days = 30
      + rotation_enabled        = (known after apply)
      + rotation_lambda_arn     = (known after apply)
      + tags                    = {
          + "Name" = "my-key-pair--key"

      + rotation_rules {
          + automatically_after_days = (known after apply)

  # module.secretsmanager-keypair.aws_secretsmanager_secret_version.secret_key_value will be created
  + resource "aws_secretsmanager_secret_version" "secret_key_value" {
      + arn            = (known after apply)
      + id             = (known after apply)
      + secret_id      = (known after apply)
      + secret_string  = (sensitive value)
      + version_id     = (known after apply)
      + version_stages = (known after apply)

  # module.secretsmanager-keypair.tls_private_key.key will be created
  + resource "tls_private_key" "key" {
      + algorithm                  = "RSA"
      + ecdsa_curve                = "P224"
      + id                         = (known after apply)
      + private_key_pem            = (sensitive value)
      + public_key_fingerprint_md5 = (known after apply)
      + public_key_openssh         = (known after apply)
      + public_key_pem             = (known after apply)
      + rsa_bits                   = 4096

Plan: 4 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.secretsmanager-keypair.tls_private_key.key: Creating...
module.secretsmanager-keypair.tls_private_key.key: Creation complete after 1s [id=61704d4254c141508b85dd84e458118b56991a25]
module.secretsmanager-keypair.aws_key_pair.keypair: Creating...
module.secretsmanager-keypair.aws_secretsmanager_secret.secret_key: Creating...
module.secretsmanager-keypair.aws_key_pair.keypair: Creation complete after 1s [id=my-key-pair-20210411131439454400000001]
module.secretsmanager-keypair.aws_secretsmanager_secret.secret_key: Creation complete after 1s [id=arn:aws:secretsmanager:ap-south-1:123456789012:secret:my-key-pair-20210411131439454700000002-tNmhsz]
module.secretsmanager-keypair.aws_secretsmanager_secret_version.secret_key_value: Creating...
module.secretsmanager-keypair.aws_secretsmanager_secret_version.secret_key_value: Creation complete after 0s [id=arn:aws:secretsmanager:ap-south-1:123456789012:secret:my-key-pair-20210411131439454700000002-tNmhsz|4423B0C7-9538-4CD4-A0C9-386F46A4F811]

Apply complete! Resources: 4 added, 0 changed, 0 destroyed.

Your developers can now copy the private key from Secrets Manager when they need it:

About the Author

Harish KM is an AWS Developer at QloudX. He is passionate about creating zero-maintenance fully-serverless cloud-native solutions in AWS. With 20+ cloud & IT certifications, he is an expert in a multitude of technologies, especially serverless.

Leave a Reply

Your email address will not be published. Required fields are marked *