Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
# aws-ssm-ec2-proxy-command
Open an SSH connection to your ec2 instances via AWS SSM without the need to open any ssh port in you security groups.

### Windows users
In order to use this project on Windows refer to [README.windows.md](README.windows.md)
#### Prerequisits
* Local Setup
* [Install AWS CLI](https://docs.aws.amazon.com/cli/latest/userguide/cli-chap-install.html)
Expand Down
78 changes: 78 additions & 0 deletions README.windows.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
# aws-ssm-ec2-proxy-command (Windows)

Open an SSH connection to your ec2 instances via AWS SSM without the need to open any ssh port in you security groups.

#### Prerequisits

* Local Setup
* [Install AWS CLI](https://docs.aws.amazon.com/cli/latest/userguide/cli-chap-install.html)
* Windows `winget install Amazon.AWSCLI`
* [Install AWS CLI Session Manager Plugin](https://docs.aws.amazon.com/systems-manager/latest/userguide/session-manager-working-with-install-plugin.html)
* Windows `winget install Amazon.SessionManagerPlugin`
* Ensure Your IAM Permissions
* [IAM Policy Example](aws-ssm-ec2-iam-policy.json)
* `ssm:StartSession` for DocumentName: `AWS-StartSSHSession` and Target Instance
* [AWS Documentation](https://docs.aws.amazon.com/systems-manager/latest/userguide/getting-started-restrict-access-examples.html)
* `ssm:SendCommand` for DocumentName: `AWS-RunShellScript` and Target Instance
* [AWS Documentation](https://docs.aws.amazon.com/systems-manager/latest/userguide/sysman-rc-setting-up.html)
* Target Instance Setup
* [Ensure SSM Permissions](https://docs.aws.amazon.com/systems-manager/latest/userguide/setup-instance-profile.html) fo Target Instance Profile
* Ensure SSM Agent is installed (preinstalled on all AWS Linux AMIs already)
* [Install SSM Agent on Linux Instances](https://docs.aws.amazon.com/systems-manager/latest/userguide/sysman-install-ssm-agent.html)
* `yum install -y https://s3.amazonaws.com/ec2-downloads-windows/SSMAgent/latest/linux_amd64/amazon-ssm-agent.rpm & service amazon-ssm-agent restart`
* [SSM Agent on Windows Instances](https://docs.aws.amazon.com/systems-manager/latest/userguide/sysman-install-ssm-win.html)

#### Install SSH Proxy Command

- Move proxy command script [aws-ssm-ec2-proxy-command.ps1](aws-ssm-ec2-proxy-command.ps1) to `~/.ssh/aws-ssm-ec2-proxy-command.ps1`

- Ensure you are allowed to execute powershell scripts (see [Set-ExecutionPolicy](https://docs.microsoft.com/en-us/powershell/module/microsoft.powershell.security/set-executionpolicy) command)

Unfortunately on Windows is not possible to show output while running ProxyCommand, script output is interpreted as SSH banner which is available with SSH verbose options.

##### Setup SSH Config [optional]

* Add ssh config entry for aws ec2 instances to your `~/.ssh/config`. Adjust key file path if needed.

```ssh-config
host i-* mi-*
IdentityFile ~/.ssh/id_rsa
ProxyCommand powershell.exe ~/.ssh/aws-ssm-ec2-proxy-command.ps1 %h %r %p ~/.ssh/id_rsa.pub
StrictHostKeyChecking no
```

#### Open SSH Connection

* Ensure AWS CLI environemnt variables are set properly e.g.
* `export AWS_PROFILE=default` or `AWS_PROFILE=default ssh ... <INSTACEC_USER>@<INSTANCE_ID>`
* If default region does not match instance region you need to provide it
* e.g. `<INSTACEC_USER>@<INSTANCE_ID>--<INSTANCE_REGION>`

###### SSH Command with SSH Config Setup

`ssh <INSTACEC_USER>@<INSTANCE_ID>`

* e.g. `ssh ec2-user@i-1234567890`

###### SSH Command with ProxyCommand CLI Option

```powershell
ssh.exe <INSTACEC_USER>@<INSTANCE_ID> `
-i "~/.ssh/id_rsa" `
-o ProxyCommand="powershell.exe ~/.ssh/aws-ssm-ec2-proxy-command.ps1 %h %r %p ~/.ssh/id_rsa.pub"
```

## Alternative Implementation with `ec2-instance-connect`

The advantage from security perspective it that you don't need to grant `ssm:SendCommand` to users and there by the permission to execute everything as root.
Instead you only grant `ec2-instance-connect:SendSSHPublicKey` permission to a specific instance user e.g. `ec2-user`.

* Ensure [Prerequisits](#prerequisits)
* Use this [aws-ssm-ec2-proxy-command.ps1](ec2-instance-connect/aws-ssm-ec2-proxy-command.ps1) proxy command script instead
* Use this [IAM Policy Example](ec2-instance-connect/aws-ssm-ec2-iam-policy.json) instead
* `ssm:StartSession` for DocumentName: `AWS-StartSSHSession` and Target Instance
* [AWS Documentation](https://docs.aws.amazon.com/systems-manager/latest/userguide/getting-started-restrict-access-examples.html)
* `ec2-instance-connect:SendSSHPublicKey`
* [AWS Documentation](https://docs.aws.amazon.com/systems-manager/latest/userguide/sysman-rc-setting-up.html)
* You may need to adjust `ec2:osuser` to match your needs. Default osuser is `ec2-user`
* Follow [Install Guide](#install-ssh-proxy-command)
79 changes: 79 additions & 0 deletions aws-ssm-ec2-proxy-command.ps1
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
#!/usr/bin/env sh
######## Source ################################################################
#
# https://github.com/qoomon/aws-ssm-ec2-proxy-command
#
######## Usage #################################################################
# https://github.com/qoomon/aws-ssm-ec2-proxy-command/blob/master/README.md
#
# Install Proxy Command
# - Move this script to ~/.ssh/aws-ssm-ec2-proxy-command.ps1
# - Ensure you are allowed to execute powershell scripts (see Set-ExecutionPolicy command)
#
# Add following SSH Config Entry to ~/.ssh/config
# host i-* mi-*
# IdentityFile ~/.ssh/id_rsa
# ProxyCommand powershell .exe ~/.ssh/aws-ssm-ec2-proxy-command.ps1 %h %r %p ~/.ssh/id_rsa.pub
# StrictHostKeyChecking no
#
# Ensure SSM Permissions for Target Instance Profile
# https://docs.aws.amazon.com/systems-manager/latest/userguide/setup-instance-profile.html
#
# Open SSH Connection
# ssh <INSTANCE_USER>@<INSTANCE_ID>
#
# Ensure AWS CLI environment variables are set properly
# e.g. AWS_PROFILE='default' ssh ec2-user@i-xxxxxxxxxxxxxxxx
#
# If default region does not match instance region you need to provide it like this
# ssh <INSTANCE_USER>@<INSTANCE_ID>--<INSTANCE_REGION>
#
################################################################################
$ErrorActionPreference = "Stop"

$REGION_SEPARATOR = "--"

$ec2_instance_id = $args[0]
$ssh_user = $args[1]
$ssh_port = $args[2]
$ssh_public_key_path = $args[3]
$ssh_public_key = (Get-Content $ssh_public_key_path | Select-Object -first 1)
$ssh_public_key_timeout = 60


$splitted_instance = $ec2_instance_id -split $REGION_SEPARATOR

if ($splitted_instance.Length -gt 1)
{
$ec2_instance_id = $splitted_instance[0]
$env:AWS_DEFAULT_REGION = $splitted_instance[1]
}

$authorized_key = "$ssh_public_key ssm-session"
$script = @"
\"
mkdir -p ~$ssh_user/.ssh && cd ~$ssh_user/.ssh || exit 1

echo '$authorized_key' >> authorized_keys

sleep $ssh_public_key_timeout

grep -v -F '$authorized_key' authorized_keys > .authorized_keys
mv .authorized_keys authorized_keys
\"
"@

Write-Output "Add public key $ssh_public_key_path for $ssh_user at instance $ec2_instance_id for $ssh_public_key_timeout seconds"
aws ssm send-command `
--instance-ids "$ec2_instance_id" `
--document-name 'AWS-RunShellScript' `
--comment "Add an SSH public key to authorized_keys for $ssh_public_key_timeout seconds" `
--parameters commands="$script"
if($LASTEXITCODE -ne 0) { Write-Error "Failed to add public key with error $output" }

Write-Output "Start ssm session to instance $ec2_instance_id"
aws ssm start-session `
--target "$ec2_instance_id" `
--document-name 'AWS-StartSSHSession' `
--parameters "portNumber=$ssh_port"
if($LASTEXITCODE -ne 0) { Write-Error "Failed to start ssm session to instance $output" }
70 changes: 70 additions & 0 deletions ec2-instance-connect/aws-ssm-ec2-proxy-command.ps1
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
#!/usr/bin/env sh
######## Source ################################################################
#
# https://github.com/qoomon/aws-ssm-ec2-proxy-command
#
######## Usage #################################################################
# https://github.com/qoomon/aws-ssm-ec2-proxy-command/blob/master/README.md
#
# Install Proxy Command
# - Move this script to ~/.ssh/aws-ssm-ec2-proxy-command.ps1
# - Ensure you are allowed to execute powershell scripts (see Set-ExecutionPolicy command)
#
# Add following SSH Config Entry to ~/.ssh/config
# host i-* mi-*
# IdentityFile ~/.ssh/id_rsa
# ProxyCommand powershell .exe ~/.ssh/aws-ssm-ec2-proxy-command.ps1 %h %r %p ~/.ssh/id_rsa.pub
# StrictHostKeyChecking no
#
# Ensure SSM Permissions for Target Instance Profile
# https://docs.aws.amazon.com/systems-manager/latest/userguide/setup-instance-profile.html
#
# Open SSH Connection
# ssh <INSTANCE_USER>@<INSTANCE_ID>
#
# Ensure AWS CLI environment variables are set properly
# e.g. AWS_PROFILE='default' ssh ec2-user@i-xxxxxxxxxxxxxxxx
#
# If default region does not match instance region you need to provide it like this
# ssh <INSTANCE_USER>@<INSTANCE_ID>--<INSTANCE_REGION>
#
################################################################################
$ErrorActionPreference = "Stop"

$REGION_SEPARATOR = "--"

$ec2_instance_id = $args[0]
$ssh_user = $args[1]
$ssh_port = $args[2]
$ssh_public_key_path= $args[3]

$splitted_instance = $ec2_instance_id -split $REGION_SEPARATOR

if ($splitted_instance.Length -gt 1)
{
$ec2_instance_id = $splitted_instance[0]
$env:AWS_DEFAULT_REGION = $splitted_instance[1]
}

$instance_availability_zone = (
aws ec2 describe-instances `
--instance-id "$ec2_instance_id" `
--query "Reservations[0].Instances[0].Placement.AvailabilityZone" `
--output text `
)
if($LASTEXITCODE -ne 0) { Write-Error "Failed to get availability zone for instance $ec2_instance_id" }

Write-Output "Add public key $ssh_public_key_path for $ssh_user at instance $ec2_instance_id for 60 seconds"
aws ec2-instance-connect send-ssh-public-key `
--instance-id "$ec2_instance_id" `
--instance-os-user "$ssh_user" `
--ssh-public-key "file://$ssh_public_key_path" `
--availability-zone "$instance_availability_zone"
if($LASTEXITCODE -ne 0) { Write-Error "Failed to add public key for $ssh_user at instance $ec2_instance_id" }

Write-Output "Start ssm session to instance $ec2_instance_id"
aws ssm start-session `
--target "$ec2_instance_id" `
--document-name 'AWS-StartSSHSession' `
--parameters "portNumber=$ssh_port"
if($LASTEXITCODE -ne 0) { Write-Error "Failed to start ssm session to instance $ec2_instance_id" }