Skip to content

Launching a new bastion host

A bastion host is a server exposed to the internet, and acting as a security door to the entire infrastructure.

In our infrastructure, such host runs on AWS on t4g instance for sake of diversity. You'll find a step by step tutorial how to easily launch such an instance.

Launch a new instance in AWS Console

Once logged in the AWS Console under EC2 service, launch a new instance.

Select the architecture and the OS

Select Ubuntu 20.04 64-bits ARM instance.

bastion-ubuntu-20.04-64-bits-arm.png

Select the instance type

Select tg4.nano instance type. No need of a bigger instance for the bastion.

bastion-tg4-nano.png

Configure instance details

Specifically in this page, select the VPC, subnet (management subnet) and ensure a public IP is assigned to the node.

bastion-instance-details.png

At the bottom of this page, use the User data field to customize the instance at startup time using Cloud Init features.

In the example below you can set the $USERNAME you want and its ssh public key $SSH_PUBLIC_KEY, the ssh port $SSH_PORT you want your instance to listen on, etc...

#cloud-config

# Enable password authentication with the SSH daemon
ssh_pwauth: false

users:
  - name: $USERNAME
    shell: /bin/bash
    lock_passwd: false
    chpasswd: { expire: false }
    ssh_authorized_keys:
      - $SSH_PUBLIC_KEY

packages:
  - apt-transport-https
  - ca-certificates
  - gnupg-agent
  - software-properties-common
  - openssh-server
  - fail2ban
  - python2 # required for sshuttle, since python3.8 is not compatible, see https://github.com/sshuttle/sshuttle/issues/381

runcmd:
  - systemctl stop snapd.socket && systemctl disable snapd.socket
  - systemctl disable snapd.service && systemctl disable snapd.service
  - systemctl disable snap.amazon-ssm-agent.amazon-ssm-agent.service && systemctl disable snap.amazon-ssm-agent.amazon-ssm-agent.service
  - apt autoremove -y --purge snapd
  - DEBIAN_FRONTEND=noninteractive dpkg-reconfigure --priority=low unattended-upgrades

write_files:
  - path: /etc/systemd/timesyncd.conf
    permissions: '0644'
    owner: root:root
    content: |
      [Time]
      NTP=169.254.169.123
  - path: /etc/ssh/sshd_config.d/bastion.conf
    permissions: '0644'
    owner: root:root
    content: |
      Port $SSH_PORT
      PermitRootLogin no
      AllowUsers $USERNAME

package_update: true
package_upgrade: true

power_state:
  mode: reboot

Please note in this config, the bastion host won't have any user with sudo privileges, meaning you won't be able to install any packages on it. You'll need to create another bastion host if you want to change anything.

Add storage

Very little storage is required, i.e. the default 8GB is more than enough. Mind enabling disk encryption though.

bastion-strorage.png

Add tags

A tag Name is recommended, such as $project-bastion-$indice-g-$yyyymmdd (g to mention it's a Graviton2 instance, a.k.a t4g)

bastion-tags.png

Add security groups

This part is rather specific to your setup, but the idea is to have at least one security group allowing inbound SSH traffic on your custom port to your instance.

We're using two security groups on our side:

  • $project-ssh-inbound-only, which allows inbound traffic on our specific SSH port
  • $project-is-bastion, which allows outbound SSH traffic to the other hosts in the infrastructure.

If you randomize the SSH port, the wizard might warn you don't have any rule to allow access on port 22, but that's ok, we know what we're doing.

Launch!

You can now launch your instance. A final step to select the key pairs is required. Since we provide th key pair via Cloud Init, you can choose to proceed without a key pair.

bastion-proceed-without-keypair.png

SSH config

Last but not least, a bit of configuration of your ssh setup is required to access seamlessly the bastion and the underlying hosts.

Edit your ~/.ssh/config file or equivalent, and add the bastion host and another one to use the bastion.

$BASTION_HOST_PUBLIC_IP is the bastion's public IP assigned at launch time $SSH_PRIVATE_KEY_PATH is the path to the private key corresponding to the public key $SSH_PUBLIC_KEY we set in the cloud-init file.

We assume there is another host, called host-01, which will be reachable using its private IP through the bastion.

Host bastion-01
    Hostname $BASTION_HOST_PUBLIC_IP
    Port $SSH_PORT
    User $USERNAME
    PreferredAuthentications publickey
    IdentityFile $SSH_PRIVATE_KEY_PATH

Host host-01
    ProxyJump bastion-01
    Hostname $HOST_01_PRIVATE_IP
    User $USERNAME
    PreferredAuthentications publickey
    IdentityFile $SSH_PRIVATE_KEY_PATH

Sshuttle

Sshuttle is a poor man's VPN, allowing to access private ip addresses using SSH. It fits perfectly in the bastion host's setup, allowing to grant access of nodes behind the bastion.

Assuming your VPC private network is 10.232.0.0/16, use it the following way:

sshuttle -r bastion-01 --python /usr/bin/python2 10.232.0.0/16

And now you can safely access http://$validator-private-ip:7500 to access your validator dashboard.