Bastion Host

Bastion: A well-fortified position; a stronghold or citadel.

You need a Bastion Host (in a public subnet) on AWS if you need to SSH into EC2 (or other) instances that are in a private subnet on AWS. You would use a security group to allow ssh access to the Bastion Host and a security group to allow SSH from the Bastion Host to hosts in private subnets.

https://aws.amazon.com/blogs/security/securely-connect-to-linux-instances-running-in-a-private-amazon-vpc/

SSH Tunnel via Bastion Host

Assumes ssh-agent is already running for your session. This tends to be the case for Ubuntu sessions.

Identify your key...

ssh-add -L

Add this key to ~/.ssh/authorized_keys on all hosts, including the Bastion server, that you will access.

Add something like this to ~/.ssh/config on your local linux machine...

Host bastion.com

    ForwardAgent yes

At this point you should be able to ssh to the Bastion server then ssh to the target server without the need for password entry (i.e. a two step process)...

ssh user@bastion

ssh user@destination

We should also be able to use a command like the one below to complete both steps from a single command...

ssh user@bastion.com ssh user@destination.com

NOTE: With AWS, this will likely not work and you will get this error..."Pseudo-terminal will not be allocated because stdin is not a terminal."

Add an extra stanza to ~/.ssh/config...

Host destination.com

    ProxyCommand ssh -A user@bastion.com -W %h:%p

Worked Example using Jira CloudFormation Stack

mystack=StackName

where StackName is the top level Stack Name from CloudFormation

Set variables...

BastionIP=$(aws cloudformation describe-stacks --stack-name ${mystack} --output table \    | grep "Bastion node IP" | tr -d " " | awk -F"|" '{ print $6 }')
for iid in $(aws ec2 describe-instance-status --output yaml | grep InstanceId | tr -d ' ' | awk -F: '{ print $2 }')do  aws ec2 describe-instances --instance-ids ${iid} --output table | grep PrivateIpAddress | head -1 | sed 's/|//g' | tr -d ' ' | sed 's/ss/ss /'  str=aws:cloudformation:logical-id  aws ec2 describe-instances --instance-ids ${iid} --output table | grep "${str}" | sed 's/|//g' | tr -d ' ' | eval sed 's/${str}//'done | sed '/PrivateIpAddress/{N;/\n.*Bastion/D}' | sed '/Bastion/D' | sed '/ClusterNodeGroup/D' | sed 's/PrivateIpAddress //' >/tmp/$$.tmpIFS=$'\r\n' GLOBIGNORE='*' command eval 'JNODE=($(cat /tmp/$$.tmp))'echo ${BastionIP}for a in ${JNODE[@]}; do echo ${a}; done

Add your key to the ssh agent. 

If you are not on the server where you created this key initially then copy it to .ssh under your home directory first.

cd ~/.ssh

ssh-add $(whoami).pem

i.e. use the PEM file from the EC2 Key Pair

ssh-add -L

The key should be there

Create an ssh config file...

Note: This will destroy to config file; if you are already using it for unrelated ssh config you should manually update the file using the commands below as a guide only.

echo "Host ${BastionIP}" >~/.ssh/config

echo "   ForwardAgent yes" >>~/.ssh/config

for c in ${!JNODE[@]}

do

   echo "Host ${JNODE[${c}]}" >>~/.ssh/config

   echo "    ProxyCommand ssh -A ec2-user@${BastionIP} -W %h:%p" >>~/.ssh/config

done

You should now be able to connect directly to any of the servers using the variables containing the IP addresses or the IP addresses themselves...

ssh ec2-user@${BastionIP}

ssh ec2-user@${JNODE[0]}

ssh ec2-user@${JNODE[1]}

If ssh-agent is not running, start it with...eval $(/usr/bin/ssh-agent)
Note that this will clear any variables you have set. Make sure you reset them using the steps above before attempting to create the ssh tunnels as shown to the left.
If your ssh connection times out there may be some network configuration (firewall/routing?) blocking you.




(Optional) Test connection to BastionIP...ssh ec2-user@${BastionIP}exit
(Optional) Test connection to BastionIP and then hop to JiraIPssh ec2-user@${BastionIP}ssh ec2-user@${JNODE[0]}
To be sure you are on the correct box, perform a status check on the Jira service...systemctl status jira

Bibliography