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

如何通过IAM角色从Lambda Python(Boto)无密钥连接EC2并执行SSH命令?

可行方案:通过IAM角色让Lambda无凭据访问并操作EC2实例

Absolutely, there are two reliable ways to do this without using PEM keys or any hardcoded credentials—both leaning on IAM roles for secure, permission-controlled access. Let’s walk through them, starting with the most automation-friendly approach.

方案1:使用AWS Systems Manager (SSM) Run Command(推荐)

This is the cleanest method for executing commands on EC2 instances from Lambda, since it avoids direct SSH entirely. Instead, it uses AWS’s managed SSM service to run commands via API calls, all authenticated via IAM.

前置配置

  1. Lambda执行角色权限:
    Attach these permissions to your Lambda’s execution role (you can create a custom policy for this):

    • ssm:SendCommand: To send commands to the EC2 instance
    • ssm:GetCommandInvocation: To retrieve command execution results
    • ec2:DescribeInstances (optional): If you need to look up instances by tags/names instead of hardcoding IDs

    Example policy snippet:

    {
        "Version": "2012-10-17",
        "Statement": [
            {
                "Effect": "Allow",
                "Action": [
                    "ssm:SendCommand",
                    "ssm:GetCommandInvocation",
                    "ec2:DescribeInstances"
                ],
                "Resource": "*"
            }
        ]
    }
    
  2. EC2实例配置:

    • Attach the AmazonSSMManagedInstanceCore managed policy to the EC2 instance’s IAM role (this grants the instance permission to communicate with SSM)
    • Ensure the SSM Agent is installed (pre-installed on Amazon Linux 2/2023, Ubuntu 16.04+, and Windows Server 2016+; manual installation required for other OSes)
    • Security group: Allow outbound HTTPS (443) traffic to AWS SSM endpoints (no inbound SSH port needed—this is way more secure!)

Python (Boto3) 代码示例

import boto3
import time

def lambda_handler(event, context):
    ssm_client = boto3.client('ssm')
    instance_id = 'your-ec2-instance-id'  # Or fetch via ec2:DescribeInstances
    command = 'echo "Hello from Lambda via SSM!"'

    # Send command to the instance
    response = ssm_client.send_command(
        InstanceIds=[instance_id],
        DocumentName='AWS-RunShellScript',
        Parameters={'commands': [command]}
    )

    command_id = response['Command']['CommandId']

    # Wait for command to complete and get output
    time.sleep(5)  # Adjust wait time based on command complexity
    result = ssm_client.get_command_invocation(
        CommandId=command_id,
        InstanceId=instance_id
    )

    print(f"Command output: {result['StandardOutputContent']}")
    print(f"Command errors: {result['StandardErrorContent']}")

    return {
        'statusCode': 200,
        'body': f"Command executed successfully: {result['StandardOutputContent']}"
    }

方案2:使用EC2 Instance Connect实现无密钥SSH

If you specifically need to establish a real SSH session (not just run a single command), you can use EC2 Instance Connect to push a temporary SSH public key to the instance, then connect using the private key—all without storing PEM files.

前置配置

  1. Lambda执行角色权限:
    Add these permissions to your Lambda role:

    • ec2-instance-connect:SendSSHPublicKey: To push the temporary public key to the instance
    • ec2:DescribeInstances: To get the instance’s availability zone (required for SendSSHPublicKey)
  2. EC2实例配置:

    • Install the ec2-instance-connect package (pre-installed on Amazon Linux; for Ubuntu, run sudo apt install ec2-instance-connect)
    • Security group: Allow inbound SSH (port 22) from EC2 Instance Connect’s IP ranges (you can find these in AWS docs, or temporarily allow 0.0.0.0/0—since access is gated by IAM, this is safer than it sounds)

Python (Boto3 + Paramiko) 代码示例

You’ll need to package the paramiko library (a pure-Python SSH client) into a Lambda layer or include it in your deployment package, since it’s not part of the default Lambda runtime.

import boto3
import paramiko
import tempfile
from botocore.exceptions import ClientError

def lambda_handler(event, context):
    ec2_client = boto3.client('ec2')
    instance_id = 'your-ec2-instance-id'
    username = 'ec2-user'  # Adjust for your OS (e.g., 'ubuntu' for Ubuntu)

    # Generate temporary SSH key pair
    key = paramiko.RSAKey.generate(2048)
    private_key_str = key.get_base64()
    public_key_str = f'ssh-rsa {key.get_base64()} lambda-temp-key'

    # Get instance availability zone
    instance_info = ec2_client.describe_instances(InstanceIds=[instance_id])
    az = instance_info['Reservations'][0]['Instances'][0]['Placement']['AvailabilityZone']

    # Push public key to EC2 Instance Connect
    try:
        ec2_client.send_ssh_public_key(
            InstanceId=instance_id,
            AvailabilityZone=az,
            InstanceOSUser=username,
            SSHPublicKey=public_key_str
        )
    except ClientError as e:
        return {'statusCode': 500, 'body': f"Failed to push public key: {str(e)}"}

    # Connect via SSH using the private key
    ssh_client = paramiko.SSHClient()
    ssh_client.set_missing_host_key_policy(paramiko.AutoAddPolicy())

    try:
        # Write private key to temp file (Lambda has writable /tmp directory)
        with tempfile.NamedTemporaryFile(mode='w', delete=False) as f:
            f.write(f"-----BEGIN RSA PRIVATE KEY-----\n{private_key_str}\n-----END RSA PRIVATE KEY-----\n")
            temp_key_path = f.name

        ssh_client.connect(
            hostname=instance_info['Reservations'][0]['Instances'][0]['PublicIpAddress'],
            username=username,
            key_filename=temp_key_path,
            timeout=10
        )

        # Execute command
        stdin, stdout, stderr = ssh_client.exec_command('echo "Hello from Lambda via EC2 Instance Connect!"')
        output = stdout.read().decode('utf-8')
        error = stderr.read().decode('utf-8')

        print(f"SSH command output: {output}")
        print(f"SSH command errors: {error}")

        return {'statusCode': 200, 'body': f"Command executed: {output}"}
    except Exception as e:
        return {'statusCode': 500, 'body': f"SSH connection failed: {str(e)}"}
    finally:
        ssh_client.close()
        import os
        os.unlink(temp_key_path)

关键注意事项

  • Security: Both methods rely on IAM permissions to control access, so you can restrict which Lambdas/instances can interact with each other using resource-level permissions in your policies.
  • SSM vs EC2 Instance Connect: Use SSM for simple command execution (it’s more secure and requires less setup). Use EC2 Instance Connect only if you need a full SSH session.
  • Lambda Layers: For the EC2 Instance Connect approach, remember to include paramiko in your deployment package or as a Lambda layer—you can build it using a Linux environment (since Lambda runs on Amazon Linux).

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.22 10:06:17