AWS setup
A dedicated IAM user with three permissions. None of them can start, stop, modify or read the contents of an instance.
Do not use your root account or an administrator key. If a phone is lost, the blast radius should be "could have opened a shell on instances that already allow this user", not "could do anything".
1. Create the policy
IAM → Policies → Create policy → JSON:
{
"Version": "2012-10-17",
"Statement": [
{
"Sid": "VerifyIdentity",
"Effect": "Allow",
"Action": "sts:GetCallerIdentity",
"Resource": "*"
},
{
"Sid": "FindInstanceAddresses",
"Effect": "Allow",
"Action": "ec2:DescribeInstances",
"Resource": "*"
},
{
"Sid": "PushEphemeralKey",
"Effect": "Allow",
"Action": "ec2-instance-connect:SendSSHPublicKey",
"Resource": "arn:aws:ec2:*:*:instance/*"
}
]
}
ec2:DescribeInstances does not support resource-level restriction, which is why it is *. It reveals instance metadata — IDs, addresses, tags — so use an account whose inventory you are comfortable with the app being able to list.
Restricting it further
Scope key pushing to tagged instances:
"Resource": "arn:aws:ec2:*:*:instance/*",
"Condition": {
"StringEquals": { "aws:ResourceTag/MobileAccess": "yes" }
}
Then tag only the instances you want reachable from the phone.
2. Create the user
IAM → Users → Create user. No console access. Attach the policy, then create an access key for "Application running outside AWS".
3. Add it to the app
Settings → AWS. Enter the access key ID, secret access key and region, then tap Test. A successful test shows the account ID it authenticated as. The secret is sealed with the device Keystore.
4. Add a host
Add a host, choose AWS, and enter the instance ID (i-0123456789abcdef0). Leave the hostname blank — the address is resolved at connect time, so it survives an instance restart. The username prefills as ec2-user; change it to ubuntu for Ubuntu images or admin for Debian.
Requirements on the instance
- The EC2 Instance Connect package, preinstalled on Amazon Linux 2 and later and on recent Ubuntu images.
- Port 22 reachable from your phone, via a security group.
If your instances are in a private subnet, Instance Connect alone will not reach them.
If it fails
- "Permission denied (publickey)"
- Usually the wrong username. The key push succeeded but the account it was pushed for does not match.
- Test fails with an authorisation error
- The policy is not attached, or the key belongs to a different account.
- Connection times out
- A security group or network ACL, not the app. The key push will have succeeded.