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

EC2实例间SSH连接失败求助(Terraform部署Ansible节点)

问题描述

我通过Terraform在AWS EC2上部署两台实例,分别作为Ansible控制节点和受控节点,需求是自动完成控制节点的Ansible安装、SSH密钥配置,以及将控制节点公钥复制到受控节点的authorized_keys文件中。

部署过程无报错,但尝试通过私有IP从控制节点连接受控节点时,SSH通信被拒绝,错误信息如下:

ssh ubuntu@10.10.0.29
Load key "/home/ubuntu/.ssh/id_rsa": Permission denied
ubuntu@10.10.0.29: Permission denied (publickey).

相关配置文件及权限信息如下:

VPC与安全组配置(vpc.tf)

resource "aws_vpc" "vpc_virginia" {
  cidr_block = var.virginia_cidr
  tags = {
    "Name" : "vpc_virginia"
  }
}

resource "aws_subnet" "public_subnet" {
  vpc_id                  = aws_vpc.vpc_virginia.id
  cidr_block              = var.subnets[0]
  map_public_ip_on_launch = true
  tags = {
    "Name" = "public_subnet"
  }
}

resource "aws_internet_gateway" "igw" {
  vpc_id = aws_vpc.vpc_virginia.id

  tags = {
    Name = "igw vpc virginia"
  }
}

resource "aws_route_table" "public_crt" {
  vpc_id = aws_vpc.vpc_virginia.id

  route {
    cidr_block = "0.0.0.0/0"
    gateway_id = aws_internet_gateway.igw.id
  }

  tags = {
    Name = "public crt"
  }
}

resource "aws_route_table_association" "crta_subnet" {
  subnet_id      = aws_subnet.public_subnet.id
  route_table_id = aws_route_table.public_crt.id
}

resource "aws_security_group" "sg_control_node" {
  name        = "public_instance-sg"
  description = "Allow SSH inbound traffic and all egress traffic"
  vpc_id      = aws_vpc.vpc_virginia.id

  ingress {
    description = "SSH over Internet"
    from_port   = 22
    to_port     = 22
    protocol    = "tcp"
    cidr_blocks = [var.sg_ingress_cidr]
  }

  egress {
    from_port        = 0
    to_port          = 0
    protocol         = "-1"
    cidr_blocks      = ["0.0.0.0/0"]
    ipv6_cidr_blocks = ["::/0"]
  }

  tags = {
    Name = "control-node_sg"
  }
}

resource "aws_security_group" "sg_managed_node" {
  name        = "managed-node-sg"
  description = "Allow SSH inbound traffic and all HTTP traffic"
  vpc_id      = aws_vpc.vpc_virginia.id

  ingress {
    description = "SSH over Internet"
    from_port   = 22
    to_port     = 22
    protocol    = "tcp"
    cidr_blocks = [var.sg_ingress_cidr]
  }

  ingress {
    description = "HTTP traffic"
    from_port   = 80
    to_port     = 80
    protocol    = "tcp"
    cidr_blocks = [var.sg_ingress_cidr]
  }

  egress {
    from_port        = 0
    to_port          = 0
    protocol         = "-1"
    cidr_blocks      = ["0.0.0.0/0"]
    ipv6_cidr_blocks = ["::/0"]
  }

  tags = {
    Name = "managed_node_sg"
  }
}

EC2与密钥复制配置(ec2.tf)

resource "aws_instance" "ec2_control" {
  ami                    = "ami-0c7217cdde317cfec"
  instance_type          = "t2.micro"
  subnet_id              = aws_subnet.public_subnet.id
  key_name               = data.aws_key_pair.key.key_name
  vpc_security_group_ids = [aws_security_group.sg_control_node.id]

  provisioner "remote-exec" {
    inline = [
      "sudo apt-get update -y",
      "sudo apt-get install -y software-properties-common",
      "sudo apt-add-repository --yes --update ppa:ansible/ansible",
      "sudo apt-get install -y ansible",
      "sudo adduser ansible --disabled-password --gecos ''",
      "sudo ssh-keygen -t rsa -b 4096 -f /home/ubuntu/.ssh/id_rsa -N ''",
    ]

    connection {
      type        = "ssh"
      host        = self.public_ip
      user        = "ubuntu"
      private_key = file("keys/mykey.pem")
    }
  }
  tags = {
    Name = "control_node"
  }
}

resource "aws_instance" "ec2_managed" {
  ami                    = "ami-0c7217cdde317cfec"
  instance_type          = "t2.micro"
  subnet_id              = aws_subnet.public_subnet.id
  key_name               = data.aws_key_pair.key.key_name
  vpc_security_group_ids = [aws_security_group.sg_managed_node.id]

  tags = {
    Name = "managed_node"
  }
}

resource "null_resource" "copy_public_key" {
  provisioner "local-exec" {
    command = "scp -o StrictHostKeyChecking=no -i keys/mykey.pem ubuntu@${aws_instance.ec2_control.public_ip}:/home/ubuntu/.ssh/id_rsa.pub public_key.txt"
  }

  provisioner "remote-exec" {
    inline = [
      "echo '${file("public_key.txt")}' >> /home/ubuntu/.ssh/authorized_keys"
    ]

    connection {
      type        = "ssh"
      user        = "ubuntu"
      private_key = file("keys/mykey.pem")
      host        = aws_instance.ec2_managed.public_ip
    }
  }

  depends_on = [aws_instance.ec2_control, aws_instance.ec2_managed]
}

节点权限信息

控制节点.ssh目录权限

drwx------ 2 ubuntu ubuntu 4096 Feb  9 03:57 .
drwxr-x--- 4 ubuntu ubuntu 4096 Feb  9 03:56 ..
-rw------- 1 ubuntu ubuntu  387 Feb  9 03:56 authorized_keys
-rw------- 1 root   root   3389 Feb  9 03:57 id_rsa
-rw-r--r-- 1 root   root    745 Feb  9 03:57 id_rsa.pub

受控节点.ssh目录权限

drwx------ 2 ubuntu ubuntu 4096 Feb  9 03:56 .
drwxr-x--- 4 ubuntu ubuntu 4096 Feb  9 03:57 ..
-rw------- 1 ubuntu ubuntu 1133 Feb  9 03:58 authorized_keys

问题根源分析
  1. SSH密钥权限错误:控制节点上生成的id_rsa和id_rsa.pub属于root用户,而连接受控节点时使用的是ubuntu用户,ubuntu没有读取root所属密钥文件的权限,导致加载密钥失败。这是因为生成密钥时使用了sudo ssh-keygen,命令以root身份执行,生成的文件归属root。
  2. 安全组规则限制:受控节点的安全组仅允许来自var.sg_ingress_cidr的SSH流量,而控制节点通过私有IP发起连接,该IP不在允许范围内,即使密钥正确也会被安全组拦截。

解决方案

1. 修正控制节点密钥归属权限

修改控制节点的remote-exec脚本,去掉sudo执行ssh-keygen,或者生成后修改文件归属:

provisioner "remote-exec" {
  inline = [
    "sudo apt-get update -y",
    "sudo apt-get install -y software-properties-common",
    "sudo apt-add-repository --yes --update ppa:ansible/ansible",
    "sudo apt-get install -y ansible",
    "sudo adduser ansible --disabled-password --gecos ''",
    # 去掉sudo,以ubuntu用户身份生成密钥
    "ssh-keygen -t rsa -b 4096 -f /home/ubuntu/.ssh/id_rsa -N ''",
    # 或者保留sudo,添加修改归属的命令
    # "sudo ssh-keygen -t rsa -b 4096 -f /home/ubuntu/.ssh/id_rsa -N ''",
    # "sudo chown ubuntu:ubuntu /home/ubuntu/.ssh/id_rsa /home/ubuntu/.ssh/id_rsa.pub"
  ]

  connection {
    type        = "ssh"
    host        = self.public_ip
    user        = "ubuntu"
    private_key = file("keys/mykey.pem")
  }
}

2. 调整受控节点安全组规则

修改sg_managed_node的SSH ingress规则,允许控制节点所在的VPC CIDR或者控制节点的安全组访问:

resource "aws_security_group" "sg_managed_node" {
  name        = "managed-node-sg"
  description = "Allow SSH from control node/VPC and all HTTP traffic"
  vpc_id      = aws_vpc.vpc_virginia.id

  ingress {
    description = "SSH from control node security group"
    from_port   = 22
    to_port     = 22
    protocol    = "tcp"
    # 允许控制节点安全组的流量
    security_groups = [aws_security_group.sg_control_node.id]
  }

  ingress {
    description = "HTTP traffic"
    from_port   = 80
    to_port     = 80
    protocol    = "tcp"
    cidr_blocks = [var.sg_ingress_cidr]
  }

  egress {
    from_port        = 0
    to_port          = 0
    protocol         = "-1"
    cidr_blocks      = ["0.0.0.0/0"]
    ipv6_cidr_blocks = ["::/0"]
  }

  tags = {
    Name = "managed_node_sg"
  }
}

3. 优化密钥复制流程(可选)

直接在控制节点使用ssh-copy-id复制公钥到受控节点,避免本地文件中转,更可靠:

resource "null_resource" "copy_public_key" {
  provisioner "remote-exec" {
    inline = [
      # 先信任受控节点主机密钥,再复制公钥
      "ssh-keyscan ${aws_instance.ec2_managed.private_ip} >> /home/ubuntu/.ssh/known_hosts",
      "ssh-copy-id -i /home/ubuntu/.ssh/id_rsa.pub ubuntu@${aws_instance.ec2_managed.private_ip}"
    ]

    connection {
      type        = "ssh"
      user        = "ubuntu"
      private_key = file("keys/mykey.pem")
      host        = aws_instance.ec2_control.public_ip
    }
  }

  depends_on = [aws_instance.ec2_control, aws_instance.ec2_managed]
}

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.30 10:23:12