An SSH command has only four visible pieces—client, key, username, and host—but EC2 adds a route, public/private addressing, security groups, network ACLs, status checks, AMI defaults, and host identity. Verify those pieces before changing sshd or opening the instance to the internet.

Quick connection command

Terminalbash
chmod 400 ~/.ssh/my-ec2-key.pem
ssh -o IdentitiesOnly=yes -i ~/.ssh/my-ec2-key.pem ec2-user@PUBLIC_DNS_NAME

Replace every example value

  • my-ec2-key.pem must be the private key matching the public key trusted by the instance; moving it under ~/.ssh is safer than leaving credentials in Downloads.

  • chmod 400 allows only the file owner to read the key. OpenSSH rejects private keys readable by other users.

  • ec2-user is correct for Amazon Linux 2023 and Amazon Linux 2, but not every AMI.

  • PUBLIC_DNS_NAME is the instance’s current reachable DNS name, not its Name tag or an arbitrary hostname.

  • IdentitiesOnly=yes tells OpenSSH to offer the specified/configured identity instead of every key loaded in an agent.

  • On first connection, verify the displayed host-key fingerprint through a trusted AWS/administrative path before accepting it.

Choose the access method before configuring SSH

  • Systems Manager Session Manager: managed Linux/Windows shell with IAM authorization and no inbound port when the SSM agent, instance profile, and network endpoints/outbound access are configured.

  • EC2 Instance Connect: IAM-authorized delivery of a short-lived SSH public key; a public path or EC2 Instance Connect Endpoint and supported instance configuration are still required.

  • OpenSSH with a static key: simple and widely compatible, but requires key custody, reachable TCP 22, host-key verification, rotation, and Linux account lifecycle.

  • EC2 Instance Connect Endpoint: reaches private-IP instances without assigning them public IPs; IAM, endpoint compatibility, and security-group rules govern the tunnel.

  • Bastion/VPN/Direct Connect: appropriate in some network designs, but introduces another system and trust boundary that must be patched, audited, and restricted.

SSH trust and network path

ec2-ssh-path.txttext
Your OpenSSH client
  ├── reads private key + local config
  ├── resolves public/private DNS
  └── TCP connection to port 22
          │ route / IGW / VPN / endpoint / bastion
          │ security group + network ACL + guest firewall
      EC2 instance sshd
          ├── proves server identity with host key
          ├── maps requested Linux username
          └── checks matching public key in authorized_keys
              interactive shell

Each arrow can fail independently; the SSH banner is reached only after the TCP path succeeds.

Use the error phase as evidence

  • DNS failure occurs before a TCP attempt; verify the address and resolver path.

  • Timeout occurs before SSH authentication; inspect addressing, route, ingress, NACL, firewall, instance health, and listener.

  • Host-key warning occurs during server identity verification; investigate replacement/readdressing or possible interception before changing known_hosts.

  • Permission denied (publickey) means an SSH server answered but rejected the username/key/authentication configuration. Opening security groups further will not fix it.

  • A shell that disconnects later points to keepalive, idle policy, resource pressure, reboot, network change, or session tooling—not initial key permissions.

Prerequisites

  • The instance is running and both EC2 system/instance status checks pass.

  • You know the exact account, Region, instance ID, AMI/publisher, OS, and intended Linux login user.

  • The selected address is reachable from your client: public DNS/IP for direct internet SSH, or private DNS/IP through an approved private path.

  • The instance security group permits TCP 22 only from your actual source/path. Custom NACL and guest firewall rules also allow the connection and return traffic.

  • sshd is installed/running in the AMI and the matching public key is authorized for the selected user.

  • The private key is intact, protected, not revoked, and available only to authorized operators.

  • You have a trusted method to verify the instance host-key fingerprint before accepting it.

1. Retrieve the authoritative instance details

Terminalbash
aws ec2 describe-instances \
  --instance-ids INSTANCE_ID \
  --region REGION \
  --query "Reservations[0].Instances[0].{State:State.Name,AMI:ImageId,PublicDNS:PublicDnsName,PublicIP:PublicIpAddress,PrivateDNS:PrivateDnsName,PrivateIP:PrivateIpAddress,Subnet:SubnetId,Groups:SecurityGroups[*].GroupId,Key:KeyName}" \
  --output json

Why this is better than copying an old address

  • Replace INSTANCE_ID and REGION; IDs and public DNS details are Region-scoped.

  • A stop/start commonly changes an auto-assigned public IPv4 address and public DNS mapping. Elastic IPs/load balancers/DNS records provide different stability models.

  • An empty public address is expected for a private instance; use the approved private access method instead of assigning public exposure reflexively.

  • The EC2 key-pair name identifies launch metadata, not a downloadable copy of your private key and not necessarily later guest changes.

  • Inspect the returned security-group rules separately; knowing group IDs does not prove port 22 is authorized from the correct source.

2. Confirm the default username from the AMI

  • Amazon Linux 2023 and Amazon Linux 2 normally use ec2-user.

  • Ubuntu AMIs normally use ubuntu.

  • Debian AMIs commonly use admin.

  • CentOS can use centos or ec2-user; RHEL documentation can specify ec2-user or root depending on the image.

  • Other marketplace/community/custom AMIs define their own account. Verify the publisher documentation or image build, and never infer the username from the instance type.

  • Linux users are guest OS identities. They are not IAM users, IAM roles, or the email identity used for the AWS console.

3. Protect and identify the private key

Terminalbash
install -d -m 700 ~/.ssh
mv ~/Downloads/my-ec2-key.pem ~/.ssh/my-ec2-key.pem
chmod 400 ~/.ssh/my-ec2-key.pem
ssh-keygen -y -f ~/.ssh/my-ec2-key.pem | ssh-keygen -lf -

Private-key takeaways

  • install -d -m 700 creates a private SSH directory if absent.

  • mv removes the example key from Downloads; verify the source path and backup/custody policy before running it. Moving is recoverable only from the destination, not a duplicate.

  • chmod 400 is owner-read-only on POSIX filesystems. Windows OpenSSH uses Windows ACLs rather than this mode command.

  • ssh-keygen -y derives the public key in memory/stdout; the second command prints its fingerprint without uploading the private key.

  • A local public-key fingerprint helps inventory keys but does not prove the instance currently trusts that public key.

  • Never paste a private key into an online fingerprint tool, ticket, chat, source repository, user data, or shared shell history.

4. Restrict the security group

  • For direct SSH from one current IPv4 address, authorize TCP 22 from YOUR_PUBLIC_IP/32. For IPv6 use the exact approved IPv6 CIDR.

  • If your organization exits through a stable corporate/VPN CIDR, authorize that range according to policy. Residential addresses can change.

  • For an EC2 Instance Connect Endpoint, authorize the endpoint path according to AWS’s target security-group requirements—not the operator’s arbitrary public IP.

  • For a bastion, reference the bastion security group where supported rather than opening the private instance to the internet.

  • Review every security group attached to the ENI; AWS applies the union of their allow rules, so another broad group can defeat your narrow rule.

  • Security groups are stateful, while custom NACLs are stateless and must permit both connection and response traffic.

5. Verify the host key before trusting it

The private key proves your client to the server; the SSH host key proves the server to your client. On first connection, OpenSSH shows a fingerprint because no trusted mapping exists yet. Compare it with a fingerprint obtained through an independent trusted channel—such as approved EC2 console output, Session Manager, image/provisioning records, or an administrator already connected securely.

6. Connect with OpenSSH

Terminalbash
ssh -o IdentitiesOnly=yes \
  -i ~/.ssh/my-ec2-key.pem \
  ec2-user@ec2-198-51-100-10.compute-1.amazonaws.com

Command semantics

  • -i selects the private identity file; its public half must be authorized for the remote user.

  • IdentitiesOnly=yes prevents an SSH agent containing many keys from causing “too many authentication failures.”

  • The example hostname uses documentation-only address space and will not reach your instance. Substitute the current verified DNS/IP.

  • The user@host value controls the guest account. A correct key offered to the wrong account is rejected.

  • OpenSSH uses port 22 unless -p or configuration says otherwise; changing sshd’s port also requires matching network rules and operational documentation.

Use an SSH config for repeatable access

~/.ssh/configsshconfig
Host lynxbee-lab-ec2
    HostName ec2-198-51-100-10.compute-1.amazonaws.com
    User ec2-user
    IdentityFile ~/.ssh/my-ec2-key.pem
    IdentitiesOnly yes
    StrictHostKeyChecking yes

A named host prevents operators from repeatedly copying long commands and accidentally offering unrelated keys.

Configuration takeaways

  • Run ssh lynxbee-lab-ec2 after replacing the documentation hostname.

  • StrictHostKeyChecking yes refuses unknown/changed keys instead of training users to accept them blindly; provision verified host keys into known_hosts first.

  • Keep ~/.ssh/config private (chmod 600) because hostnames and usernames reveal infrastructure metadata even though it should contain no private-key material.

  • Use separate aliases/keys for environments and include files managed by approved tooling for larger fleets.

  • Static hostnames are a poor fleet-discovery system; dynamic environments should use managed access and inventory automation.

Connect to a private instance

  • Session Manager: no public address or inbound SSH rule; requires SSM agent, instance role permissions, and SSM network connectivity/endpoints.

  • EC2 Instance Connect Endpoint: creates a managed tunnel to the private address and can use AWS CLI v2/console with IAM authorization.

  • VPN or Direct Connect: your client routes to VPC private space; DNS, routes, security groups, NACLs, and corporate egress policy must align.

  • Bastion/ProxyJump: SSH first traverses a hardened jump host. Restrict source/destination and avoid copying private keys onto the bastion—agent forwarding itself has security tradeoffs.

  • Do not assign a public IP merely because a tutorial only shows public SSH. Private-by-default access is usually easier to defend.

EC2 Instance Connect Endpoint example

Terminalbash
aws ec2-instance-connect ssh \
  --instance-id INSTANCE_ID \
  --os-user ec2-user \
  --connection-type eice \
  --region REGION

Managed-tunnel takeaways

  • This requires AWS CLI version 2, an EC2 Instance Connect Endpoint, compatible addressing, IAM authorization, and correct endpoint/instance security-group rules.

  • The instance must support the required SSH/Instance Connect integration for the selected connection workflow.

  • --connection-type eice explicitly selects the private endpoint path instead of relying on future-changing automatic selection order.

  • The SSH session persists until closed even if the initiating IAM credentials later expire; apply session-duration and monitoring controls where required.

  • CloudTrail records Instance Connect API requests, but guest commands require separate shell/session/audit logging.

Troubleshooting by failure phase

  • Could not resolve hostname: wrong/stale DNS, typo, private DNS outside its resolver context, or resolver/network failure.

  • Connection timed out: wrong address/Region, instance not healthy, no route/public IP/IGW/VPN/endpoint, source changed, security group/NACL/firewall blocks, or sshd not listening.

  • Connection refused: host is reachable but port has no listener or an active reject; inspect sshd/service/firewall and custom port configuration.

  • UNPROTECTED PRIVATE KEY FILE: fix local ownership/permissions; do not weaken the client check.

  • Permission denied (publickey): wrong username, key, authorized_keys content/permissions, AMI, Instance Connect integration, or sshd authentication policy.

  • Too many authentication failures: too many agent identities were offered; use IdentitiesOnly=yes and the intended -i key.

  • REMOTE HOST IDENTIFICATION HAS CHANGED: stop and verify instance lifecycle, address reassignment, DNS and fingerprint; do not reflexively run ssh-keygen -R.

  • Session drops/freezes: inspect network path, idle timeouts, client/server keepalive policy, instance resource pressure, reboot/events, and logs.

Test name resolution and TCP 22 separately

Terminalbash
getent ahosts PUBLIC_DNS_NAME
nc -vz -w 5 PUBLIC_DNS_NAME 22

Reachability-test takeaways

  • Replace the hostname. getent shows addresses selected through the client’s configured name-service path.

  • nc attempts a TCP connection without authenticating; success proves a listener/path responded, not that its SSH host key or your user/key is correct.

  • A timeout still cannot identify which network control dropped traffic; compare route, source address, security groups, NACLs, firewall, and listener evidence.

  • Some environments lack netcat or block probes. OpenSSH verbose output remains the authoritative next check.

Collect verbose diagnostics safely

Terminalbash
ssh -vvv -o IdentitiesOnly=yes \
  -i ~/.ssh/my-ec2-key.pem \
  ec2-user@PUBLIC_DNS_NAME

How to read and share verbose output

  • Verbose output shows configuration files, DNS/address choice, connection phase, host-key algorithms, and identities offered/accepted.

  • It does not print the private key, but it can expose usernames, hostnames, IPs, key fingerprints, local paths, proxy commands, banners, and infrastructure policy. Redact deliberately.

  • Look for the last completed phase rather than searching only for the final generic error.

  • Use -vvv for a controlled reproduction, then remove it from normal workflows to avoid excessive sensitive logging.

After login

Terminalbash
cat /etc/os-release
id
who
printf "SSH source: %s\n" "$SSH_CONNECTION"
sudo -n true && echo "passwordless sudo permitted" || echo "sudo requires interaction or is denied"

Post-login verification

  • Confirm the OS/build and identity match the intended instance before making changes.

  • $SSH_CONNECTION shows client/server addresses and ports for the current session; treat it as operational metadata.

  • sudo -n true tests non-interactive sudo policy without prompting. A permitted result is authority, not a reason to run every command as root.

  • Check patch state, time, disk, agents, logs, and workload health using the approved runbook.

  • End the session with exit, remove temporary ingress when used, and rotate/revoke exposed keys.

Key and account lifecycle

  • Do not share one private key among people. Give each operator an identity and auditable authorization path.

  • Rotate by installing/testing a new public key or managed access route before removing the old one; keep break-glass procedures controlled and tested.

  • EC2 cannot re-download a generated private key. Losing it does not require exposing the instance; use an existing managed access/recovery path or approved volume-repair workflow.

  • Removing a key pair object from EC2 does not automatically remove public keys already written into guest authorized_keys.

  • Terminate access promptly when ownership changes, investigate any exposure, and keep host keys/known_hosts lifecycle aligned with instance replacement.

Primary references