You can do every step of launching a server correctly and still be locked out of it. The instance is up, the key is right, and SSH still times out, because a firewall rule you never set is quietly turning you away. Day 22 closed the first stretch of this challenge on exactly that lesson.
One Linux task, one AWS task. Clone a Git repository into a target directory, then launch an EC2 instance with SSH key access and fix the security group when the connection is refused. The tasks come from the KodeKloud Engineer platform.
Git clone: two forms, one rule about the directory
# Clone into the current directory (must be empty)
cd /path/to/target
git clone <repo-url> .
# Or clone into a new named subdirectory (must not exist yet)
git clone <repo-url> directory-name
git log --oneline
Two forms, and the difference is the directory. git clone <url> . clones into the directory you are already in, which has to be empty. git clone <url> name creates a new subdirectory of that name, which must not already exist. Mix them up, and Git refuses rather than making a mess, which is the polite kind of failure. git log --oneline afterwards confirms the history actually came down.
EC2 with SSH: the launch is easy, the security group is the catch
The AWS side had more to it. Generate a key, import it to AWS, launch an instance with it, then connect. The launch used two patterns worth calling out:
# Latest Amazon Linux 2 AMI, straight from Parameter Store (never stale)
AMI_ID=$(aws ssm get-parameter \
--name "/aws/service/ami-amazon-linux-latest/amzn2-ami-hvm-x86_64-gp2" \
--query "Parameter.Value" --output text)
# Import your existing public key, then launch with it
aws ec2 import-key-pair --key-name id_rsa \
--public-key-material fileb:///root/.ssh/id_rsa.pub
Two good habits here. Pulling the AMI ID from SSM Parameter Store means you always launch on the current Amazon Linux image instead of hardcoding one that goes stale, the exact problem I flagged back on Day 6. And import-key-pair uses fileb:// rather than file://, because a key is binary, so fileb reads raw bytes where file reads text.
Then the launch succeeds, the instance is running, and SSH hangs. The instance is fine. The problem is that the security group, which is the instance's firewall, has no rule allowing inbound port 22, so your connection never gets through. This is the single most common reason a fresh EC2 rejects you, and the fix is one rule:
# Your own public IP
MY_IP=$(curl -s https://checkip.amazonaws.com)
# Allow SSH from just your IP, not the whole internet
aws ec2 authorize-security-group-ingress \
--group-id $SG_ID --protocol tcp --port 22 --cidr "${MY_IP}/32"
The important detail is the /32. Opening port 22 to 0.0.0.0/0 would let the entire internet knock on your SSH door. Scoping it to your own IP with /32 means only your machine can reach it, least privilege applied to a firewall rule. The scripted connect also uses ssh -o StrictHostKeyChecking=no to skip the host-key prompt. That is fine inside automation where nothing can answer a prompt, but know what it gives up: that check is your protection against a man-in-the-middle, so it is a convenience with a real tradeoff, not a free one.
The last mile is access
Both tasks came down to the last mile, the small thing between "set up" and "actually reachable." The clone works or fails on one empty directory. The server is useless until one firewall rule lets you in. Everything upstream can be perfect, and the last rule is what decides whether you get to use any of it.
That is Day 22; the pattern across all of them has been the same one: the command is the easy part, and the thing it quietly depends on is where the real learning sits.
So here is the Day 22 question. Would you rather build everything and discover the access gap when you cannot get in, or check the last mile first, before you need it?
Day 22 down. Seventy-eight to go.
Top comments (0)