diff --git a/README.md b/README.md index a5dcd6b..e39910e 100644 --- a/README.md +++ b/README.md @@ -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) diff --git a/README.windows.md b/README.windows.md new file mode 100644 index 0000000..32fb657 --- /dev/null +++ b/README.windows.md @@ -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 ... @` +* If default region does not match instance region you need to provide it + * e.g. `@--` + +###### SSH Command with SSH Config Setup + +`ssh @` + +* e.g. `ssh ec2-user@i-1234567890` + +###### SSH Command with ProxyCommand CLI Option + +```powershell +ssh.exe @ ` +-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) diff --git a/aws-ssm-ec2-proxy-command.ps1 b/aws-ssm-ec2-proxy-command.ps1 new file mode 100755 index 0000000..fd1811f --- /dev/null +++ b/aws-ssm-ec2-proxy-command.ps1 @@ -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 @ +# +# 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 @-- +# +################################################################################ +$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" } \ No newline at end of file diff --git a/ec2-instance-connect/aws-ssm-ec2-proxy-command.ps1 b/ec2-instance-connect/aws-ssm-ec2-proxy-command.ps1 new file mode 100755 index 0000000..31435bd --- /dev/null +++ b/ec2-instance-connect/aws-ssm-ec2-proxy-command.ps1 @@ -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 @ +# +# 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 @-- +# +################################################################################ +$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" } \ No newline at end of file