告别堡垒机:EC2 Instance Connect Endpoint 零公网IP连私有子网
公司有一堆 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'
这个命令做了什么:
open-tunnel通过 亚马逊云科技 API 建立一条隧道到目标实例- SSH 流量走这条隧道,不经过公网
- 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 内流量计。
我的踩坑记录
-
创建后等 3 分钟:状态变
create-complete才能用,急着连会报错。 -
安全组方向搞反:EICE 安全组要配出站规则,目标实例安全组配入站规则。很多人把 EICE 的入站配了,没用。
-
EC2 Instance Connect 要装:Amazon Linux 2/2023 自带,Ubuntu 需要
sudo apt install ec2-instance-connect。不装的话推公钥不生效。 -
子网不能是纯 IPv6:EICE 目前需要 IPv4 子网或双栈子网。
-
每个 VPC 最多 5 个:一般够用,但多环境共用 VPC 的话要规划好。
什么时候用 EICE,什么时候用 SSM Session Manager
两个都能解决"不暴露公网 IP 连实例"的问题:
| 对比 | EICE | SSM Session Manager |
|---|---|---|
| 协议 | SSH/RDP(标准端口) | 专有通道 |
| 客户端 | 标准 SSH | 亚马逊云科技 CLI + SSM 插件 |
| 文件传输 | SCP/SFTP | 需要额外配置 |
| 端口转发 | 原生支持 | 支持 |
| Agent | 不需要 | 需要装 SSM Agent |
| 记录 | CloudTrail | CloudTrail + 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
更多推荐
所有评论(0)