如何使用Terraform向GCP实例添加SSH密钥?

32

我有一个用于在Google Cloud Platform创建实例的terraform脚本,我希望能够让我的terraform脚本将我的ssh密钥添加到我创建的实例中,以便我可以通过ssh对它们进行配置。以下是我的当前terraform脚本。

#PROVIDER INFO
provider "google" {
  credentials = "${file("account.json")}"
  project     = "myProject"
  region      = "us-central1"
}


#MAKING CONSUL SERVERS
resource "google_compute_instance" "default" {
  count    =  3
  name     =  "a-consul${count.index}"
  machine_type = "n1-standard-1"
  zone         = "us-central1-a"

  disk {
    image = "ubuntu-1404-trusty-v20160627"
  }

  # Local SSD disk
  disk {
    type    = "local-ssd"
    scratch = true
  }

  network_interface {
    network = "myNetwork"
    access_config {}
  }
}

我需要在我的 terraform 脚本中添加什么内容才能够添加我的 SSH 密钥 /Users/myUsername/.ssh/id_rsa.pub

10个回答

49

嗨,我有一个关于这个问题的快速跟进,它与添加ssh密钥无关,但可能会使用gcp terraform实例的“metadata”功能。我希望terraform为我的实例添加“--can-ip-forward”标记,我该怎么做? - Alex Cohen
1
看起来你只需要在资源中添加 can_ip_forward = true:https://github.com/iostat/mesos-fun/blob/bb08bc4866a1ba4e535cbff4eba6611f1d3838b4/terraform/3-slaves.tf 和 https://www.terraform.io/docs/providers/google/r/compute_instance.html - mblakele
7
注意,"sshKeys"现在已被弃用,当前的语法是"ssh-keys"。 - Jean-Bernard Jansen
1
使用此方法时,我能够创建实例并登录,但如果我转到GCP控制台并尝试编辑实例,则会显示ssh-keys错误,我认为这可能是因为密钥末尾应该存在用户名。错误:SSH密钥格式不正确。 - Abhilasha
1
请注意,写作ssh-keys而不是ssh_keys,虽然两者都被接受,但它们执行不同的操作。您最有可能需要的是ssh-keys而不是ssh_keysssh_keys安装了您的云帐户密钥,而ssh-keys则安装了指定文件中的密钥。 - Christian Hujer
显示剩余2条评论

18

仅供记录。从0.12版本开始,该区块应该如下所示:

resource "google_compute_instance" "default" {
  # ...

  metadata = {
    ssh-keys = join("\n", [for user, key in var.ssh_keys : "${user}:${key}"])
  }

  # ...
}

(注意在 metadata 标记后面的等号符号=,以及 ssh-keyssshKeys 的区别)。


当你将"${user}:${key}"替换为"${var.user}:${var.key}"时,这将起作用,虽然我认为传递密钥列表有些过度 - 我们应该努力创建永久基础设施,不需要任何人ssh连接。 - volvox
1
为什么这里需要添加“用户名”?不能只传递ssh密钥本身而不需要“用户名”吗? - Jananath Banuka
@JananathBanuka 这是 GCP 要求的,因为默认用户名可能会随着您使用的镜像而有所不同(如 ubuntu、debian、centos、rhel 等)。 - webofmars

7
您可以使用以下内容。
metadata = {
  ssh-keys = "username:${file("username.pub")}"
}

我在使用terraform创建实例时遇到了困难,通过这个答案的测试和工作,我成功地使用ssh密钥创建了实例。


为什么我们需要添加“用户名”?不能只传递“ssh”密钥本身而不需要“用户名”吗? - Jananath Banuka
没有用户名,Terraform 怎么知道如何通过 SSH 或使用服务器登录? - sambit
干净利落,但无法添加多个键。 - Widmo
@widmo 你需要使用多个带有不同用户名的pub文件,并使用循环函数一次性添加多个密钥。 - sambit
@sambit 好的,谢谢。我希望我可以有一个文件包含许多公钥(针对一个用户),谢谢! - Widmo

5
这是一个测试样例。
  metadata {
    sshKeys = "${var.ssh_user}:${var.ssh_key} \n${var.ssh_user1}:${var.ssh_key1}"
}

为什么我们需要添加“用户名”?不能只传递“ssh”密钥本身而不需要“用户名”吗? - Jananath Banuka

5
如果您需要多个键,可以像这样使用 heredoc
  metadata = {
    "ssh-keys" = <<EOT
<user>:<key>
<user>:<key>
EOT
  }

我保留了terraform fmt提供的奇怪格式,此处的内容与IT技术有关。

3

Terraform v0.15.4 中更新多个键的方法如下:

metadata = {
    ssh-keys = join("\n", [for key in var.ssh_keys : "${key.user}:${key.publickey}"])
}

根据变量:

variable "ssh_keys" {
  type = list(object({
    publickey = string
    user = string
  }))
  description = "list of public ssh keys that have access to the VM"
  default = [
      {
        user = "username"
        publickey = "ssh-rsa yourkeyabc username@PC"
      }
  ]
}

请问您可以告诉我您是如何获取私钥路径的吗? - user15224751
@RohanKumarJangid 是哪个私钥路径?我没有处理任何私钥。 - Daniel Habenicht
为了连接到 GCP 机器,需要一个私钥参数,但我不知道该怎么使用它。我是否需要创建它或需要进行什么操作?如需正确提问,请参阅 https://stackoverflow.com/questions/68206325/unable-to-run-metadata-startup-script-in-terraform。 - user15224751

2

我已经让以下内容为我工作:对于所有虚拟机,使用单个SSH密钥

resource "google_compute_project_metadata" "my_ssh_key" {
  metadata = {
    ssh-keys = <<EOF
      terakey:ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAICqaF7TqtimTUtqLdZIspKjuTXXXXnkbW7N9TQBPXazu terakey
      
    EOF
  }
}

0

需要知道的一件事是,如果有更高级别的项目,您可能需要向元数据中添加一个选项来禁用操作系统登录。

metadata = {
    "enable-oslogin" = false
    "ssh-keys"       = ...
}

只有在此之后,authorized_keys 文件才会被创建。

另外,您可能需要添加 "can_ip_forward = true" 选项以启用外部流量。


0

我测试了以下将SSH公钥注入到Google计算实例的方法,对我来说有效。

  metadata = {
    ssh-keys = "${var.ssh_user}:${file("./gcp_instance_ssh_key.pub")}"
  OR 
    ssh-keys  = "${var.ssh_user}:${file(var.public_key_path)}"

  OR
    ssh-keys  = "${var.ssh_user}:${file("${var.public_key_path}")}"
   
  }

variable "public_key_path" {
    default = "./gcp_instance_ssh_key.pub"   ##public key with path
}

请注意使用ssh-keys而不是ssh_keys(带下划线)

0

首先,您需要一个计算实例:

resource "google_compute_instance" "website_server" {
  name                      = "webserver"
  description               = "Web Server"
  machine_type              = "f1-micro"
  allow_stopping_for_update = true
  deletion_protection       = false

  tags = ["webserver-instance"]

  shielded_instance_config {
    enable_secure_boot          = true
    enable_vtpm                 = true
    enable_integrity_monitoring = true
  }

  scheduling {
    provisioning_model  = "STANDARD"
    on_host_maintenance = "TERMINATE"
    automatic_restart   = true
  }

  boot_disk {
    mode        = "READ_WRITE"
    auto_delete = true
    initialize_params {
      image = "ubuntu-minimal-2204-jammy-v20220816"
      type  = "pd-balanced"
    }
  }

  network_interface {
    network = "default"

    access_config {
      network_tier = "PREMIUM"
    }
  }

  metadata = {
    ssh-keys               = "${var.ssh_user}:${local_file.public_key.content}"
    block-project-ssh-keys = true
  }

  labels = {
    terraform = "true"
    purpose   = "host-static-files"
  }

  service_account {
    # Custom service account with restricted permissions
    email  = data.google_service_account.myaccount.email
    scopes = ["compute-rw"]
  }

}

请注意,元数据中的“ssh-keys”字段需要以“授权密钥”格式提供公钥数据,即开放式SSH公钥。这类似于执行“pbcopy <~/.ssh/id_ed25519.pub”的操作。
您需要一个防火墙规则来允许默认端口22上的SSH连接:
resource "google_compute_firewall" "webserver_ssh" {
  name    = "webserver-firewall"
  network = "default"

  allow {
    protocol = "tcp"
    ports    = ["22"]
  }

  target_tags   = ["webserver-instance"]
  source_ranges = ["0.0.0.0/0"]
}

您的公钥和私钥可以是短暂的,以使事情更加无缝:

resource "tls_private_key" "webserver_access" {
  algorithm = "ED25519"
}

resource "local_file" "public_key" {
  filename        = "server_public_openssh"
  content         = trimspace(tls_private_key.webserver_access.public_key_openssh)
  file_permission = "0400"
}

resource "local_sensitive_file" "private_key" {
  filename = "server_private_openssh"
  # IMPORTANT: Newline is required at end of open SSH private key file
  content         = tls_private_key.webserver_access.private_key_openssh
  file_permission = "0400"
}

最后,要登录您需要基于以下内容的连接字符串:

output "instance_connection_string" {
  description = "Command to connect to the compute instance"
  value       = "ssh -i ${local_sensitive_file.private_key.filename} ${var.ssh_user}@${google_compute_instance.website_server.network_interface.0.access_config.0.nat_ip} ${var.host_check} ${var.ignore_known_hosts}"
  sensitive   = false
}

变量file可能看起来像:

variable "ssh_user" {
  type        = string
  description = "SSH user for compute instance"
  default     = "myusername"
  sensitive   = false
}

variable "host_check" {
  type        = string
  description = "Dont add private key to known_hosts"
  default     = "-o StrictHostKeyChecking=no"
  sensitive   = false
}

variable "ignore_known_hosts" {
  type        = string
  description = "Ignore (many) keys stored in the ssh-agent; use explicitly declared keys"
  default     = "-o IdentitiesOnly=yes"
  sensitive   = false
}


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