公司有一堆 EC2 跑在 Private Subnet 里,没有公网 IP。每次运维要先连 VPN,再跳到堡垒机,再 SSH 到目标实例。三层跳转,光认证就要两分钟。

堡垒机还得维护——打补丁、轮换密钥、监控登录日志。说白了,它本身就是个攻击面。

后来发现亚马逊云科技出了 EC2 Instance Connect Endpoint(EICE),可以直接从本地 SSH 到 Private Subnet 的实例,不需要公网 IP、不需要堡垒机、不需要 VPN。用 IAM 做认证,连密钥管理都省了。

这篇记录下从零搭建的过程。

EICE 是什么?一张图说清

你的电脑 → AWS API → EICE Endpoint(VPC内部)→ 私有子网 EC2

关键点:

  • EICE 是亚马逊云科技托管的资源,创建在你的 VPC 里
  • 流量走 亚马逊云科技 内部网络,EC2 不需要公网 IP
  • 认证走 IAM,不是 SSH 密钥(虽然也可以用密钥)
  • 免费——没有额外费用

传统方式 vs EICE:

对比项堡垒机EICE
公网 IP需要不需要
维护成本需打补丁、监控零维护
认证方式SSH 密钥IAM 策略
审计自己搞日志CloudTrail 自动记录
费用EC2 费用免费
攻击面暴露公网零暴露

第一步:创建 EICE

用 CLI 创建,指定 VPC 和子网:

aws ec2 create-instance-connect-endpoint \
    --subnet-id subnet-0123456789abcdef0 \
    --security-group-ids sg-0123456789abcdef0 \
    --region ap-northeast-1

或者用 Python:

import boto3

ec2 = boto3.client('ec2', region_name='ap-northeast-1')

response = ec2.create_instance_connect_endpoint(
    SubnetId='subnet-0123456789abcdef0',
    SecurityGroupIds=['sg-0123456789abcdef0'],
    PreserveClientIp=False
)

endpoint_id = response['InstanceConnectEndpoint']['InstanceConnectEndpointId']
print(f'EICE 创建中: {endpoint_id}')
print(f'状态: {response["InstanceConnectEndpoint"]["State"]}')

创建大概需要 2-3 分钟。状态从 create-in-progress 变成 create-complete 就好了。

查看状态:

response = ec2.describe_instance_connect_endpoints(
    InstanceConnectEndpointIds=[endpoint_id]
)

state = response['InstanceConnectEndpoints'][0]['State']
print(f'当前状态: {state}')

几个注意事项:

  • 每个 VPC 最多 5 个 EICE
  • EICE 要创建在和目标实例同一个 VPC 里,但不一定同一个子网
  • 安全组要允许 EICE 访问目标实例的 22 端口(SSH)

第二步:配安全组

EICE 的安全组规则很简单——出站放通到目标实例:

ec2_resource = boto3.resource('ec2', region_name='ap-northeast-1')

# EICE 的安全组:出站放通 SSH
eice_sg = ec2_resource.SecurityGroup('sg-eice-xxx')
eice_sg.authorize_egress(
    IpPermissions=[{
        'IpProtocol': 'tcp',
        'FromPort': 22,
        'ToPort': 22,
        'UserIdGroupPairs': [{'GroupId': 'sg-target-xxx'}]
    }]
)

# 目标实例的安全组:入站允许来自 EICE 安全组的 SSH
target_sg = ec2_resource.SecurityGroup('sg-target-xxx')
target_sg.authorize_ingress(
    IpPermissions=[{
        'IpProtocol': 'tcp',
        'FromPort': 22,
        'ToPort': 22,
        'UserIdGroupPairs': [{'GroupId': 'sg-eice-xxx'}]
    }]
)

用安全组引用而不是 IP 范围——这样不管 EICE 的私有 IP 怎么变,规则都生效。

第三步:配 IAM 权限

这是 EICE 的精髓——用 IAM 控制谁能连哪台机器:

{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Sid": "AllowEICEConnect",
            "Effect": "Allow",
            "Action": [
                "ec2-instance-connect:OpenTunnel"
            ],
            "Resource": "arn:aws:ec2:ap-northeast-1:123456789012:instance-connect-endpoint/*",
            "Condition": {
                "NumericLessThanEquals": {
                    "ec2-instance-connect:maxTunnelDuration": "3600"
                }
            }
        },
        {
            "Sid": "AllowSSHConnect",
            "Effect": "Allow",
            "Action": [
                "ec2-instance-connect:SendSSHPublicKey"
            ],
            "Resource": "arn:aws:ec2:ap-northeast-1:123456789012:instance/*",
            "Condition": {
                "StringEquals": {
                    "ec2:osuser": "ec2-user"
                }
            }
        },
        {
            "Sid": "DescribeEndpoints",
            "Effect": "Allow",
            "Action": [
                "ec2:DescribeInstanceConnectEndpoints",
                "ec2:DescribeInstances"
            ],
            "Resource": "*"
        }
    ]
}

亮点:

  • maxTunnelDuration:限制单次连接时长,最多 1 小时自动断开
  • ec2:osuser:限制只能用 ec2-user 登录,不能 root
  • 可以用 ec2:ResourceTag 条件键限制只能连特定标签的实例

比如只允许连 Environment=staging 的实例:

{
    "Condition": {
        "StringEquals": {
            "ec2:ResourceTag/Environment": "staging"
        }
    }
}

这比堡垒机的权限控制精细太多了——堡垒机上你能 SSH 到任何实例,EICE 可以按标签、按实例 ID 精确控制。

第四步:连接

方法一:控制台直连

EC2 控制台 → 选实例 → Connect → EC2 Instance Connect → Connect using Private IP → 选 EICE → 连接。

浏览器里直接开终端,最方便。

方法二:CLI + SSH

ssh -i my-key.pem ec2-user@i-0123456789abcdef0 \
    -o ProxyCommand='aws ec2-instance-connect open-tunnel \
    --instance-id i-0123456789abcdef0'

这个命令做了什么:

  1. open-tunnel 通过 亚马逊云科技 API 建立一条隧道到目标实例
  2. SSH 流量走这条隧道,不经过公网
  3. IAM 认证在 open-tunnel 这一步完成

方法三:不用密钥

EC2 Instance Connect 可以推送临时公钥,60 秒有效:

aws ec2-instance-connect send-ssh-public-key \
    --instance-id i-0123456789abcdef0 \
    --instance-os-user ec2-user \
    --ssh-public-key file://~/.ssh/id_rsa.pub

推完公钥后 60 秒内 SSH 连接,过期自动失效。不用长期管理密钥对。

方法四:SSH Config 配置

写到 ~/.ssh/config 里,以后直连:

Host staging-*
    User ec2-user
    IdentityFile ~/.ssh/my-key.pem
    ProxyCommand aws ec2-instance-connect open-tunnel --instance-id %n

然后 ssh staging-i-0123456789abcdef0 就行了。

审计

每次通过 EICE 连接都会在 CloudTrail 留记录:

import boto3

cloudtrail = boto3.client('cloudtrail', region_name='ap-northeast-1')

response = cloudtrail.lookup_events(
    LookupAttributes=[
        {
            'AttributeKey': 'EventName',
            'AttributeValue': 'OpenTunnel'
        }
    ],
    MaxResults=10
)

for event in response['Events']:
    print(f"时间: {event['EventTime']}")
    print(f"用户: {event['Username']}")
    print(f"IP: {event['Resources'][0]['ResourceName'] if event.get('Resources') else 'N/A'}")
    print('---')

谁、什么时间、连了哪台机器,全部自动记录。堡垒机上你得自己配 session recording,还得担心日志被篡改。

成本

免费。EICE 不收费,数据传输按正常 VPC 内流量计。

我的踩坑记录

  1. 创建后等 3 分钟:状态变 create-complete 才能用,急着连会报错。

  2. 安全组方向搞反:EICE 安全组要配出站规则,目标实例安全组配入站规则。很多人把 EICE 的入站配了,没用。

  3. EC2 Instance Connect 要装:Amazon Linux 2/2023 自带,Ubuntu 需要 sudo apt install ec2-instance-connect。不装的话推公钥不生效。

  4. 子网不能是纯 IPv6:EICE 目前需要 IPv4 子网或双栈子网。

  5. 每个 VPC 最多 5 个:一般够用,但多环境共用 VPC 的话要规划好。

什么时候用 EICE,什么时候用 SSM Session Manager

两个都能解决"不暴露公网 IP 连实例"的问题:

对比EICESSM Session Manager
协议SSH/RDP(标准端口)专有通道
客户端标准 SSH亚马逊云科技 CLI + SSM 插件
文件传输SCP/SFTP需要额外配置
端口转发原生支持支持
Agent不需要需要装 SSM Agent
记录CloudTrailCloudTrail + S3/CloudWatch

简单说:习惯 SSH 工作流的用 EICE,想要更完整的运维管控用 SSM。

以上代码基于亮马逊云科技 EC2 Instance Connect Endpoint,CLI 和 Python boto3 验证通过。


📌 EICE 完全免费,每个 VPC 最多 5 个,支持 SSH 和 RDP。
🔗 文档:https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/connect-using-eice.html

更多推荐