How to Transfer SSH Public Key to EC2 Instance Without Using ec2-* CLI Tools


2 views

When working with AWS EC2 instances, there are scenarios where you need to transfer your SSH public key without relying on AWS-specific CLI tools like ec2-instance-connect or aws ec2-instance commands. This typically happens when:

  • Your IAM permissions don't include EC2 API access
  • You're working in an environment with strict security policies
  • You need to script the process in a vendor-agnostic way

Method 1: Using SSH Directly

The most reliable way is to use standard SSH authentication to first connect with an existing key, then append your new public key:

# First connect using existing credentials
ssh -i ~/.ssh/existing_key.pem ec2-user@your-instance-public-dns

# Once connected, append your new public key
echo "ssh-rsa AAAAB3NzaC1yc2EAAA... your-comment" >> ~/.ssh/authorized_keys

Method 2: Temporary Password Authentication

If you can temporarily enable password authentication:

# On the EC2 instance (via AWS console or existing access):
sudo sed -i 's/PasswordAuthentication no/PasswordAuthentication yes/' /etc/ssh/sshd_config
sudo systemctl restart sshd

# Then use ssh-copy-id with password:
ssh-copy-id -i ~/.ssh/new_key.pub ec2-user@your-instance-public-dns

# Remember to disable password auth afterward

Method 3: EC2 User Data Injection

For new instances, inject the public key through user data:

#!/bin/bash
mkdir -p /home/ec2-user/.ssh
echo "ssh-rsa AAAAB3NzaC1yc2EAAA..." >> /home/ec2-user/.ssh/authorized_keys
chown -R ec2-user:ec2-user /home/ec2-user/.ssh
chmod 700 /home/ec2-user/.ssh
chmod 600 /home/ec2-user/.ssh/authorized_keys

Common pitfalls include incorrect permissions on the .ssh directory or authorized_keys file. The directory should be 700 and the file 600:

ssh -i ~/.ssh/existing_key.pem ec2-user@instance \
"chmod 700 ~/.ssh; chmod 600 ~/.ssh/authorized_keys"

For scripting purposes, here's a complete workflow:

#!/bin/bash
INSTANCE_DNS="ec2-1-2-3-4.compute-1.amazonaws.com"
TEMP_KEY="~/.ssh/temp_access.pem"
NEW_PUB_KEY="~/.ssh/new_key.pub"

# Copy via existing credentials
scp -i $TEMP_KEY $NEW_PUB_KEY ec2-user@$INSTANCE_DNS:/tmp/new_key.pub

# SSH in and append the key
ssh -i $TEMP_KEY ec2-user@$INSTANCE_DNS \
"cat /tmp/new_key.pub >> ~/.ssh/authorized_keys && rm /tmp/new_key.pub"

When provisioning EC2 instances, we often need to transfer SSH public keys manually when:

  • AWS CLI/API tools aren't available
  • Existing key pairs need rotation
  • Automation scripts require alternative methods

Traditional approaches like ssh-copy-id or scp fail because:


# This doesn't work as expected:
ssh-copy-id -i ~/.ssh/new_key.pub ec2-user@ec2-instance

# Neither does this:
scp -i ~/.ssh/existing_key.pem ~/.ssh/new_key.pub ec2-user@ec2-instance:~/.ssh/

The -i parameter behaves differently across tools - for ssh-copy-id it specifies which key to install, not which key to authenticate with.

Method 1: SSH Pipe Technique


cat ~/.ssh/new_key.pub | ssh -i ~/.ssh/existing_key.pem ec2-user@ec2-instance \
"mkdir -p ~/.ssh && cat >> ~/.ssh/authorized_keys && chmod 700 ~/.ssh && chmod 600 ~/.ssh/authorized_keys"

Method 2: Temporary SCP Transfer


# First copy the public key to a temporary location
scp -i ~/.ssh/existing_key.pem ~/.ssh/new_key.pub ec2-user@ec2-instance:/tmp/new_key.pub

# Then SSH in to move it properly
ssh -i ~/.ssh/existing_key.pem ec2-user@ec2-instance \
"cat /tmp/new_key.pub >> ~/.ssh/authorized_keys && rm /tmp/new_key.pub"

For automation scenarios, consider this Python script using Paramiko:


import paramiko

def add_public_key(hostname, username, key_path, public_key_path):
    private_key = paramiko.RSAKey.from_private_key_file(key_path)
    ssh = paramiko.SSHClient()
    ssh.set_missing_host_key_policy(paramiko.AutoAddPolicy())
    ssh.connect(hostname, username=username, pkey=private_key)
    
    sftp = ssh.open_sftp()
    try:
        with sftp.file('.ssh/authorized_keys', 'a') as f:
            with open(public_key_path) as pk:
                f.write(pk.read() + '\n')
    finally:
        sftp.close()
    ssh.close()

Always verify the key was properly installed:


ssh -i ~/.ssh/new_key ec2-user@ec2-instance "echo 'Key test successful'"

Check the remote authorized_keys file:


ssh -i ~/.ssh/existing_key.pem ec2-user@ec2-instance \
"cat ~/.ssh/authorized_keys | grep 'your_key_comment'"