如何在Terraform中创建SSH密钥?

133
我需要为不同的用户启动一堆EC2实例。每个用户应该与其他用户隔离,因此每个EC2实例都需要有自己的SSH密钥。
在Terraform中,最好的方法是如何实现这一点?
几乎所有我找到的指南都要求我手动创建一个SSH密钥并将其粘贴到terraform脚本中。
(糟糕的)示例:
- [链接1](https://github.com/hashicorp/terraform/issues/1243) - [链接2](http://2ninjas1blog.com/terraform-assigning-an-aws-key-pair-to-your-ec2-instance-resource/) - [链接3](Terraform fails to import key pair with Amazon EC2)
由于我需要为许多用户自动生成唯一的密钥,这种方法是不切实际的。
这似乎不是一个困难的使用案例,但我无法在任何地方找到相关文档。
如果必要的话,我可以使用Bash生成Terraform脚本并动态注入SSH密钥。但这似乎正是Terraform本应该做的事情。

4
通常,这些用户会提供其公钥,因此您不需要生成任何内容。为什么让您的用户自己去管理大量的私钥呢?(注:原文中的"headache"表示“麻烦事”或“困难”,这里译为“让您的用户自己去管理大量的私钥”来表达意思) - jarmod
长话短说,这绝对是一个要求。这个terraform构建的一部分是为了一个合同,我们也处理用户管理。 - Abe
6个回答

212

Terraform可以使用tls_private_key资源生成SSL/SSH私钥。

因此,如果您想要动态生成SSH密钥,可以执行以下操作:

variable "key_name" {}

resource "tls_private_key" "example" {
  algorithm = "RSA"
  rsa_bits  = 4096
}

resource "aws_key_pair" "generated_key" {
  key_name   = var.key_name
  public_key = tls_private_key.example.public_key_openssh
}

data "aws_ami" "ubuntu" {
  most_recent = true

  filter {
    name   = "name"
    values = ["ubuntu/images/hvm-ssd/ubuntu-focal-20.04-amd64-server-*"]
  }

  filter {
    name   = "virtualization-type"
    values = ["hvm"]
  }

  owners = ["099720109477"] # Canonical
}

resource "aws_instance" "web" {
  ami           = data.aws_ami.ubuntu.id
  instance_type = "t2.micro"
  key_name      = aws_key_pair.generated_key.key_name

  tags {
    Name = "HelloWorld"
  }
}

output "private_key" {
  value     = tls_private_key.example.private_key_pem
  sensitive = true
}

这将创建一个SSH密钥对,存储在Terraform状态中(除了在不使用远程状态时可能为Terraform状态本身而写入磁盘的文件之外),基于公钥创建AWS密钥对,然后创建一个Ubuntu 20.04实例,其中ubuntu用户可以使用生成的私钥进行访问。

然后,您需要从状态文件中提取私钥并将其提供给用户。您可以在Terraform应用时使用output将其直接输出到stdout。

通过以下命令获取私钥的输出:

terraform output -raw private_key

安全注意事项

在这里我应该指出,传递私钥通常是一个坏主意,最好让开发人员创建自己的密钥对并向您提供公钥,您(或他们)可以使用它来生成 AWS 密钥对(可能使用上面示例中使用的 aws_key_pair 资源)。在创建实例时指定。

通常情况下,我只会在非常临时的开发环境中使用生成 SSH 密钥的上述方法,这样您就可以控制,不需要将私钥传递给任何人。如果您确实需要向其他人传递私钥,则需要确保您在安全信道中进行此操作,并确保 Terraform 状态(其中包含明文私钥)也得到适当的安全保护。


1
问题在于私钥无法与remote_exec一起使用。 - Archimedes Trajano
3
原始问题并不要求它随后作为执行的一部分被使用,因此您最好提出一个单独的问题,链接到本问题,并解释为什么这里的答案不适用于您的特定用例,展示您的代码以及运行时出现的错误。 - ydaetskcoR
你如何输出所有的私钥?这需要一个循环,但我不确定如何在terraform中实现。 - C.J.
2
这个答案仅生成单个私钥。如果您正在做不同的事情,则可能值得创建一个新问题,展示您所做的,并将其作为参考链接回到此问题。 - ydaetskcoR
如果您正在状态文件中生成ssh密钥,请考虑使用加密的远程状态。并且请使用“敏感”标志,以便密钥不会显示在cli输出中。阅读更多:https://www.terraform.io/docs/extend/best-practices/sensitive-state.html - Bojan Radulovic
显示剩余4条评论

34

2022年2月更新:

以下代码创建了myKeyAWSmyKey.pem您的计算机,并且创建的myKeymyKey.pem具有相同的私钥。(我使用了Terraform v0.15.4

resource "tls_private_key" "pk" {
  algorithm = "RSA"
  rsa_bits  = 4096
}

resource "aws_key_pair" "kp" {
  key_name   = "myKey"       # Create "myKey" to AWS!!
  public_key = tls_private_key.pk.public_key_openssh

  provisioner "local-exec" { # Create "myKey.pem" to your computer!!
    command = "echo '${tls_private_key.pk.private_key_pem}' > ./myKey.pem"
  }
}

在ssh到您的ec2实例之前,请运行下面的代码以确保myKey.pem只能被您阅读。

chmod 400 myKey.pem
否则会出现以下错误。
@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
@         WARNING: UNPROTECTED PRIVATE KEY FILE!          @
@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
Permissions 0664 for 'myKey.pem' are too open.
It is required that your private key files are NOT accessible by others.
This private key will be ignored.
Load key "myKey.pem": bad permissions
ubuntu@35.72.30.251: Permission denied (publickey).

我发现我需要在tls_private_key.bastion.public_key_openssh上使用trimspace,以便aws接受它。 - mcfedr
@mcfedr - 你是如何在 local-exec 配置管理器中使用 trimspace() 的? - Saurabh
@saurabh 我不使用 local-exec,我只是用键制作一个输出。 - mcfedr
警告:我使用了这个命令,在一个系统中遇到了权限错误。错误信息将整个命令以及private_key_pem的内容都显示出来了。请使用其他答案。这样做是不安全的。 - artronics

19

对之前答案的扩展,不适合在评论中:

将生成的密钥写入具有正确权限的私有文件:

resource "local_file" "pem_file" {
  filename = pathexpand("~/.ssh/${local.ssh_key_name}.pem")
  file_permission = "600"
  directory_permission = "700"
  sensitive_content = tls_private_key.ssh.private_key_pem
}

然而,这样保存文件的一个不利之处是路径最终会出现在terraform状态中。如果只是CI/CD和/或一个人运行terraform应用程序,那么这并不是什么大问题,但如果有更多的“应用者”,那么当上一个应用程序运行apply特定情况下,tfstate将在不同的人运行apply时更新。这将产生一些“更新”噪音。虽然不是很严重,但需要注意。

避免这种情况的替代方法是将pem文件保存在AWS Secrets Manager中或加密在S3中,并提供一个命令来获取它并创建本地文件。

2023年3月更新:

自2022年3月起,已经可以使用local_sensitive_file。请使用它来代替原来的方法:

resource "local_sensitive_file" "pem_file" {
  filename = pathexpand("~/.ssh/${local.ssh_key_name}.pem")
  file_permission = "600"
  directory_permission = "700"
  content = tls_private_key.ssh.private_key_pem
}

感谢 @kangkyud 指出了如何改进这篇文章!


1
我喜欢这个答案。有一件事,local_sensitive_file 出现在 https://registry.terraform.io/providers/hashicorp/local/latest/docs/resources/sensitive_file 上。 - kangkyu
1
事实上,我已经使用 local_sensitive_file 一段时间了。我更新了帖子,谢谢 @kangkyu。 - Oliver

10

补充 Kai 的回答:

variable "generated_key_name" {
  type        = string
  default     = "terraform-key-pair"
  description = "Key-pair generated by Terraform"
}

resource "tls_private_key" "dev_key" {
  algorithm = "RSA"
  rsa_bits  = 4096
}

resource "aws_key_pair" "generated_key" {
  key_name   = var.generated_key_name
  public_key = tls_private_key.dev_key.public_key_openssh

  provisioner "local-exec" {    # Generate "terraform-key-pair.pem" in current directory
    command = <<-EOT
      echo '${tls_private_key.dev_key.private_key_pem}' > ./'${var.generated_key_name}'.pem
      chmod 400 ./'${var.generated_key_name}'.pem
    EOT
  }

}


1
你必须将此与 @ydaetskcoR 的答案一起添加。
output "ssh_key" {
  description = "ssh key generated by terraform"
  value       = tls_private_key.asg_lc_key.private_key_pem
}

0
你可以使用Terraform中的tls_private_key和tls_public_key以及tls提供程序函数来创建SSH密钥对。以下是一个示例:
resource "tls_private_key" "pri_key" {
  algorithm = "RSA"
  rsa_bits  = 4096
}

resource "aws_key_pair" "keypair" {
  key_name   = "keypair"
  public_key = tls_private_key.pri_key.public_key_openssh

  provisioner "local-exec"
    command = "echo '${tls_private_key.pri_key.private_key_pem}' > ./my-keypair.pem"
  }
}

在这个例子中:
我们使用tls_private_key资源来生成RSA私钥。通过aws_key_pair.keypair,您可以创建公钥文件,然后通过使用local-exec生成密钥对文件。
不要忘记使用以下命令更新.pem文件的权限 chmod 400 ./my-keypair.pem

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接