You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何用Terraform实现EC2堡垒机SSH主机密钥在实例重部署时持久化?

解决EC2堡垒机重建时SSH主机密钥持久化的方案

针对你遇到的实例重建后SSH主机密钥变化导致验证失败的问题,有两种优雅的Terraform实现方式:

方案一:预生成固定SSH主机密钥并注入实例

直接通过Terraform生成并管理固定的SSH主机密钥,每次实例重建时都注入相同的密钥,从根源上避免密钥变化。

实现步骤

  1. 用Terraform的tls_private_key资源生成RSA和ECDSA类型的主机密钥
  2. 在user_data脚本中把密钥写入EC2实例的SSH配置目录,设置正确权限后重启sshd
  3. 输出主机公钥,方便用户一次性配置到known_hosts

Terraform代码示例

# 生成固定的SSH主机密钥
resource "tls_private_key" "ssh_host_rsa" {
  algorithm = "RSA"
  rsa_bits  = 4096
}

resource "tls_private_key" "ssh_host_ecdsa" {
  algorithm   = "ECDSA"
  ecdsa_curve = "P384"
}

# 创建堡垒机EC2实例
resource "aws_instance" "bastion" {
  ami           = "ami-xxxxxx" # 替换为你使用的AMI
  instance_type = "t2.micro"
  associate_public_ip_address = false # 绑定弹性IP,不使用临时公IP

  user_data_replace_on_change = true

  user_data = <<-EOF
              #!/bin/bash
              # 部署用户SSH密钥(原有逻辑保留)
              mkdir -p /home/ec2-user/.ssh
              echo "${var.ssh_public_keys}" >> /home/ec2-user/.ssh/authorized_keys
              chmod 600 /home/ec2-user/.ssh/authorized_keys
              chown ec2-user:ec2-user /home/ec2-user/.ssh/authorized_keys

              # 写入固定的SSH主机密钥
              cat > /etc/ssh/ssh_host_rsa_key << 'KEY'
              ${tls_private_key.ssh_host_rsa.private_key_pem}
              KEY
              cat > /etc/ssh/ssh_host_rsa_key.pub << 'KEY'
              ${tls_private_key.ssh_host_rsa.public_key_openssh}
              KEY

              cat > /etc/ssh/ssh_host_ecdsa_key << 'KEY'
              ${tls_private_key.ssh_host_ecdsa.private_key_pem}
              KEY
              cat > /etc/ssh/ssh_host_ecdsa_key.pub << 'KEY'
              ${tls_private_key.ssh_host_ecdsa.public_key_openssh}
              KEY

              # 设置密钥文件权限(sshd要求严格权限)
              chmod 600 /etc/ssh/ssh_host_*_key
              chmod 644 /etc/ssh/ssh_host_*_key.pub

              # 重启sshd服务加载新密钥
              systemctl restart sshd
              EOF
}

# 创建并关联弹性IP
resource "aws_eip" "bastion_eip" {
  vpc = true
}

resource "aws_eip_association" "bastion_eip" {
  instance_id   = aws_instance.bastion.id
  allocation_id = aws_eip.bastion_eip.id
}

# 输出主机公钥,方便用户配置known_hosts
output "ssh_host_rsa_public_key" {
  value = "${tls_private_key.ssh_host_rsa.public_key_openssh} ${aws_eip.bastion_eip.public_ip}"
}

output "ssh_host_ecdsa_public_key" {
  value = "${tls_private_key.ssh_host_ecdsa.public_key_openssh} ${aws_eip.bastion_eip.public_ip}"
}

使用时,用户只需把输出的公钥内容追加到本地~/.ssh/known_hosts文件,后续实例重建无需再修改。

方案二:用独立EBS卷持久化SSH主机密钥

将SSH主机密钥存储在单独的EBS卷中,实例重建时重新挂载该卷,保留原有密钥。

实现步骤

  1. 创建一个小型EBS卷用于存储密钥
  2. 首次启动实例时格式化卷并复制初始密钥到卷中
  3. 后续重建时直接挂载卷,确保sshd使用原有密钥

Terraform代码示例

# 创建存储SSH密钥的EBS卷
resource "aws_ebs_volume" "ssh_host_keys" {
  availability_zone = aws_instance.bastion.availability_zone
  size              = 1 # 1GB足够存储密钥文件
}

# 创建堡垒机EC2实例
resource "aws_instance" "bastion" {
  ami           = "ami-xxxxxx"
  instance_type = "t2.micro"
  associate_public_ip_address = false

  user_data_replace_on_change = true

  user_data = <<-EOF
              #!/bin/bash
              # 部署用户SSH密钥
              mkdir -p /home/ec2-user/.ssh
              echo "${var.ssh_public_keys}" >> /home/ec2-user/.ssh/authorized_keys
              chmod 600 /home/ec2-user/.ssh/authorized_keys
              chown ec2-user:ec2-user /home/ec2-user/.ssh/authorized_keys

              # 挂载EBS卷到/etc/ssh
              MOUNT_POINT="/etc/ssh"
              DEVICE="/dev/xvdf"

              # 检查卷是否已格式化,未格式化则初始化
              if ! blkid $DEVICE; then
                  mkfs.ext4 $DEVICE
                  mkdir -p $MOUNT_POINT
                  mount $DEVICE $MOUNT_POINT
                  # 复制初始生成的主机密钥到卷中
                  cp -a /etc/ssh/ssh_host_* $MOUNT_POINT/
              else
                  mkdir -p $MOUNT_POINT
                  mount $DEVICE $MOUNT_POINT
              fi

              # 写入fstab,确保开机自动挂载
              grep -q "$DEVICE" /etc/fstab || echo "$DEVICE $MOUNT_POINT ext4 defaults 0 0" >> /etc/fstab

              # 重启sshd
              systemctl restart sshd
              EOF
}

# 附加EBS卷到实例
resource "aws_volume_attachment" "ssh_keys_attach" {
  device_name = "/dev/xvdf"
  volume_id   = aws_ebs_volume.ssh_host_keys.id
  instance_id = aws_instance.bastion.id

  # 确保实例创建后再挂载卷
  depends_on = [aws_instance.bastion]
}

# 弹性IP配置(同方案一)
resource "aws_eip" "bastion_eip" {
  vpc = true
}

resource "aws_eip_association" "bastion_eip" {
  instance_id   = aws_instance.bastion.id
  allocation_id = aws_eip.bastion_eip.id
}

这个方案的优势是密钥不存储在Terraform状态中,安全性更高,但需要额外管理EBS卷。


内容的提问来源于stack exchange,提问作者RadioDevMan

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.23 14:52:03