如何通过IAM角色从Lambda Python(Boto)无密钥连接EC2并执行SSH命令?
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.
前置配置
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 instancessm:GetCommandInvocation: To retrieve command execution resultsec2: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": "*" } ] }EC2实例配置:
- Attach the
AmazonSSMManagedInstanceCoremanaged 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!)
- Attach the
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.
前置配置
Lambda执行角色权限:
Add these permissions to your Lambda role:ec2-instance-connect:SendSSHPublicKey: To push the temporary public key to the instanceec2:DescribeInstances: To get the instance’s availability zone (required for SendSSHPublicKey)
EC2实例配置:
- Install the
ec2-instance-connectpackage (pre-installed on Amazon Linux; for Ubuntu, runsudo 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)
- Install the
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
paramikoin 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

