# Creating a bastion host


If you have ever had an interest in early modern fortifications, the word _bastion_ should sound familiar to you. A bastion is a structure projecting outward from the outer wall of a fortification. Just like early modern fortresses, computer networks require multi-layer protection against external attacks. Such network bastions are called bastion hosts, and they form part of a network perimeter.

A bastion host is a virtual machine with a public IP address assigned to it to enable SSH access. Once set up, the bastion host acts as a [jump server](https://en.wikipedia.org/wiki/Jump_server) allowing you to securely connect to VMs with no public IP addresses. In this guide, you will learn how to deploy a bastion host and secure your access to remote virtual machines residing inside your virtual private cloud (VPC).

A bastion host will help you make your VPC servers less vulnerable. You will administer specific servers via a proxy connection through a bastion host over SSH.

To create a bastion host:

1. [Get your cloud ready](#before-you-begin).
1. [Create an SSH key pair](#create-ssh-keys).
1. [Create networks](#create-networks).
1. [Create security groups](#create-sg).
1. [Reserve a static public IP address](#get-static-ip).
1. [Create a virtual machine for your bastion host](#create-bastion-instance).
1. [Test your bastion host](#test-bastion).
1. [Add a virtual server to your bastion host internal segment](#add-virtual-server).
1. [Connect to the VM you created](#connect-to-instance).

If you no longer need the resources you created, [delete them](#clear-out).

![](../../_assets/tutorials/bastion-yc.svg)

## Getting started {#before-you-begin}

Sign up for Yandex Cloud and create a [billing account](../../billing/concepts/billing-account.md):
1. Navigate to the [management console](https://console.yandex.cloud) and log in to Yandex Cloud or create a new account.
1. On the **[Yandex Cloud Billing](https://center.yandex.cloud/billing/accounts)** page, make sure you have a billing account linked and it has the `ACTIVE` or `TRIAL_ACTIVE` [status](../../billing/concepts/billing-account-statuses.md). If you do not have a billing account, [create one](../../billing/quickstart/index.md) and [link](../../billing/operations/pin-cloud.md) a cloud to it.

If you have an active billing account, you can create or select a [folder](../../resource-manager/concepts/resources-hierarchy.md#folder) for your infrastructure on the [cloud page](https://console.yandex.cloud/cloud).

[Learn more about clouds and folders here](../../resource-manager/concepts/resources-hierarchy.md).

### Required paid resources {#paid-resources}

The infrastructure support cost includes:

* Fee for disks and continuously running VMs (see [Yandex Compute Cloud pricing](../../compute/pricing.md)).
* Fee for using an external IP address (see [Yandex Virtual Private Cloud pricing](../../vpc/pricing.md#prices-public-ip)).

## Create an SSH key pair {#create-ssh-keys}

To connect to a [VM](../../compute/concepts/vm.md) over SSH, you need a key pair: the public key resides on the VM, and the private one is kept by the user. This method is more secure than connecting with login and password.

{% note info %}

SSH connections using a login and password are disabled by default on public Linux images that are provided by Yandex Cloud.

{% endnote %}

Cisco Cloud Services Router (CSR) 1000v only supports keys generated using the RSA algorithm.

To create a key pair:

{% list tabs group=operating_system %}

- Linux/macOS {#linux-macos}

  1. Open the terminal.
  1. Use the `ssh-keygen` command to create a new key:

     ```bash
     ssh-keygen -t rsa -b 2048
     ```

     After you run the command, you will be asked to specify the names of files where the keys will be saved and enter the password for the private key. The default name is `id_rsa`. Keys are created in the `~/.ssh` directory.

     The public part of the key will be saved in the `<key_name>.pub` file.

- Windows 10/11 {#windows}

  1. Run `cmd.exe` or `powershell.exe`.
  1. Use the `ssh-keygen` command to create a new key:

     ```shell
     ssh-keygen -t rsa -b 2048
     ```

     After you run the command, you will be asked to specify the names of files where the keys will be saved and enter the password for the private key. The default name is `id_rsa`. The keys are created in `C:\Users\<username>\.ssh\` or `C:\Users\<username>\` depending on the command-line interface.

     The public part of the key will be saved to a file named `<key name>.pub`.

- Windows 7/8 {#windows7-8}

  Create keys using the PuTTY app:
  1. [Download](https://www.putty.org) and install PuTTY.
  1. Add the folder with PuTTY to the `PATH` variable:

      1. Click **Start** and type **Change system environment variables** in the Windows search bar.
      1. Click **Environment Variables...** at the bottom right.
      1. In the window that opens, find the `PATH` parameter and click **Edit**.
      1. Add your folder path to the list.
      1. Click **OK**.

  1. Launch the PuTTYgen app.
  1. Select **RSA** for the type of pair to generate and set the length to 2048. Click **Generate** and move the cursor in the field above it until key creation is complete.

     ![ssh_generate_key](../../_assets/compute/ssh-putty/ssh-generate-key-rsa.png)

  1. In **Key passphrase**, enter a strong password. Enter it again in the field below.
  1. Click **Save private key** and save the private key. Do not share its key phrase with anyone.
  1. Save the key to a text file. To do this, copy the public key from the text field to a text file named `id_rsa.pub`. Please note that the key must be written as a **single line** (no returns or line breaks).

{% endlist %}

{% note warning %}

Store your private key securely, as you will not be able to connect to the VM without it.

{% endnote %}

## Create an external and internal network {#create-networks}

### Create an external network and subnet {#create-external-network}

{% list tabs group=instructions %}

- Management console {#console}

  1. In the [management console](https://console.yandex.cloud), navigate to the folder where you want to create an infrastructure for your bastion host.
  1. Navigate to **Virtual Private Cloud**.
  1. In the top-right corner, click **Create network**.
  1. Specify the network name: `external-bastion-network`.
  1. Disable the **Create subnets** option.
  1. Click **Create network**.
  1. Create a subnet:

     1. Select `external-bastion-network`.
     1. At the top right, click ![image](../../_assets/console-icons/nodes-right.svg) **Create subnet**.
     1. Specify the subnet parameters:

        * **Name**: `bastion-external-segment`
        * **Availability zone**: `ru-central1-b`
        * **CIDR**: `172.16.17.0/28`

     1. Click **Create subnet**.

{% endlist %}

### Create an internal network and subnet {#create-internal-network}

{% list tabs group=instructions %}

- Management console {#console}

  1. In the [management console](https://console.yandex.cloud), navigate to the folder where you want to create an infrastructure for your bastion host.
  1. Navigate to **Virtual Private Cloud**.
  1. In the top-right corner, click **Create network**.
  1. Specify the network name: `internal-bastion-network`.
  1. Disable the **Create subnets** option.
  1. Click **Create network**.
  1. Create a subnet:

     1. Select `internal-bastion-network`.
     1. At the top right, click ![image](../../_assets/console-icons/nodes-right.svg) **Create subnet**.
     1. Specify the subnet parameters:

        * **Name**: `bastion-internal-segment`
        * **Availability zone**: `ru-central1-b`
        * **CIDR**: `172.16.16.0/24`

     1. Click **Create subnet**.

{% endlist %}

## Create security groups {#create-sg}

### Create a security group for your bastion host {#create-internet-sg}

Create a [security group](../../vpc/concepts/security-groups.md) and make the bastion host accessible from the internet by configuring its inbound traffic rules.

{% list tabs group=instructions %}

- Management console {#console}

  1. In the [management console](https://console.yandex.cloud), navigate to the folder where you want to create an infrastructure for your bastion host.
  1. Navigate to **Virtual Private Cloud** and select `external-bastion-network`.
  1. In the left-hand menu, select ![image](../../_assets/vpc/security-group.svg) **Security groups**.
  1. Click **Create security group**.
  1. Specify the security group name: `secure-bastion-sg`.
  1. Under **Rules**, navigate to the **Ingress** tab and click **Add**.
  1. Specify the rule settings:

     * **Port range**: `22`.
     * **Protocol**: `TCP`.
     * **Source**: `CIDR`.
     * **CIDR blocks**: `0.0.0.0/0`.

  1. Click **Save** in the rule creation window and in the security group creation window.

{% endlist %}

### Create a security group for internal hosts {#create-internal-hosts-sg}

Create a security group and set up rules for inbound traffic from the bastion host to internal hosts:

{% list tabs group=instructions %}

- Management console {#console}

  1. In the [management console](https://console.yandex.cloud), navigate to the folder where you want to create an infrastructure for your bastion host.
  1. Navigate to **Virtual Private Cloud** and select `internal-bastion-network`.
  1. In the left-hand menu, select ![image](../../_assets/vpc/security-group.svg) **Security groups**.
  1. Click **Create security group**.
  1. Specify the security group name: `internal-bastion-sg`.
  1. Under **Rules**, navigate to the **Ingress** tab and click **Add**.
  1. Specify the rule settings:

     * **Port range**: `22`.
     * **Protocol**: `TCP`.
     * **Source**: `CIDR`.
     * **CIDR blocks**: `172.16.16.254/32`.

  1. Click **Save** in the rule creation window.
  1. Navigate to the **Egress** tab and click **Add**.
  1. Specify the rule settings:

     * **Port range**: `22`.
     * **Protocol**: `TCP`.
     * **Destination name**: `Security group`.
     * **Security group**: `Current`.

  1. Click **Save** in the rule creation window and in the security group creation window.

{% endlist %}

## Reserve a static public IP address {#get-static-ip}

The bastion host will need a static [public IP address](../../vpc/concepts/address.md#public-addresses).

{% list tabs group=instructions %}

- Management console {#console}

  1. In the [management console](https://console.yandex.cloud), navigate to the folder where you want to create an infrastructure for your bastion host.
  1. Navigate to **Virtual Private Cloud**.
  1. In the left-hand panel, select ![image](../../_assets/vpc/ip-addresses.svg) **Public IP addresses**.
  1. Click **Reserve public IP address**.
  1. In the window that opens, select the `ru-central1-b` [availability zone](../../overview/concepts/geo-scope.md).
  1. Click ** Reserve**.
  
{% endlist %}

## Create a VM for the bastion host {#create-bastion-instance}

After you created the subnet and security group, create a virtual server for the bastion host:

{% list tabs group=instructions %}

- Management console {#console}

  1. On the [folder](../../resource-manager/concepts/resources-hierarchy.md#folder) dashboard in the [management console](https://console.yandex.cloud), click ![image](../../_assets/console-icons/plus.svg) **Create resource** and select ![image](../../_assets/console-icons/cpu.svg) **Virtual machine instance**.
  1. Under **Boot disk image**, in the **Product search** field, type `NAT instance based on Ubuntu 22.04 LTS` and select a public [NAT instance based on Ubuntu 22.04 LTS](https://yandex.cloud/en/marketplace/products/yc/nat-instance-ubuntu-22-04-lts) image.
  1. Under **Location**, select the `ru-central1-b` [availability zone](../../overview/concepts/geo-scope.md).
  1. Under **Network settings**, configure the first network interface:

      * **Subnet**: `bastion-external-segment`.
      * **Public IP address**: Click `List` and select the [IP address reserved earlier](#get-static-ip).
      * **Security groups**: `secure-bastion-sg`.

  1. Click **Add network interface** and configure the second network interface:

      * **Subnet**: `bastion-internal-segment`.
      * **Public IP address**: `No address`.
      * **Security groups**: `internal-bastion-sg`.
      * Expand the **Additional** section; in the **Internal IPv4 address** field, select `Manual`.
      * In the input field that appears, specify `172.16.16.254`.

      {% note info %}

      Make sure the first VM interface belongs to the external segment because the system will automatically use it as a default gateway.

      {% endnote %}

      In the settings above, you specified a public IP address for the external segment and internal static IP address for the internal segment.

  1. Under **Access**, select the **SSH key** option, and specify the VM access credentials:

      * In the **Login** field, specify the username: `bastion`.
      * In the **SSH key** field, select the SSH key saved in your [organization user](../../organization/concepts/membership.md) profile.
        
        If there are no SSH keys in your profile or you want to add a new key:
        
        1. Click **Add key**.
        1. Enter a name for the SSH key.
        1. Select one of the following:
        
            * `Enter manually`: Paste the contents of the public SSH key. You need to [create](../../compute/operations/vm-connect/ssh.md#creating-ssh-keys) an SSH key pair on your own.
            * `Load from file`: Upload the public part of the SSH key. You need to create an SSH key pair on your own.
            * `Generate key`: Automatically create an SSH key pair.
            
              When adding a new SSH key, an archive containing the key pair will be created and downloaded. In Linux or macOS-based operating systems, unpack the archive to the `/home/<user_name>/.ssh` directory. In Windows, unpack the archive to the `C:\Users\<user_name>/.ssh` directory. You do not need additionally enter the public key in the management console.
        
        1. Click **Add**.
        
        The system will add the SSH key to your organization user profile. If the organization has [disabled](../../organization/operations/os-login-access.md) the ability for users to add SSH keys to their profiles, the added public SSH key will only be saved in the user profile inside the newly created resource.

  1. Under **General information**, specify the VM name: `bastion-host`.
  1. Click **Create VM**.

  Once the server VM starts, its status will change to **Running** and you will see its public IP address in the **Public IP address** field.

{% endlist %}

## Test the bastion host {#test-bastion}

After you start your bastion host, try to connect to it with the SSH client:

```bash
ssh -i ~/.ssh/<name_of_private_key_file> bastion@<public_IP_address_of_bastion_host>
```

## Add a virtual server to the bastion host internal segment {#add-virtual-server}

To administer your servers, add a network interface to the internal network segment of the bastion host, `bastion-internal-segment`.

If you already have a virtual machine, [add](../../compute/operations/vm-control/attach-network-interface.md) another network interface to it. If not, create a new VM to test your bastion host configuration:

{% list tabs group=instructions %}

- Management console {#console}

  1. On the [folder](../../resource-manager/concepts/resources-hierarchy.md#folder) dashboard in the [management console](https://console.yandex.cloud), click ![image](../../_assets/console-icons/plus.svg) **Create resource** and select ![image](../../_assets/console-icons/cpu.svg) **Virtual machine instance**.
  1. Under **Boot disk image**, select a public image with any OS, e.g., [Ubuntu 22.04 LTS](https://yandex.cloud/en/marketplace/products/yc/ubuntu-22-04-lts).
  1. Under **Location**, select the `ru-central1-b` [availability zone](../../overview/concepts/geo-scope.md).
  1. Under **Network settings**, configure a network interface:

      * **Subnet**: `bastion-internal-segment`.
      * **Public IP address**: `No address`.
      * **Security groups**: `internal-bastion-sg`.
      * Expand the **Advanced** section; in the **Internal IPv4 address** field, select `Auto`.

  1. Under **Access**, select the **SSH key** option, and specify the VM access credentials:

      * In the **Login** field, specify the username: `test`.
      * In the **SSH key** field, select the SSH key saved in your [organization user](../../organization/concepts/membership.md) profile.
        
        If there are no SSH keys in your profile or you want to add a new key:
        
        1. Click **Add key**.
        1. Enter a name for the SSH key.
        1. Select one of the following:
        
            * `Enter manually`: Paste the contents of the public SSH key. You need to [create](../../compute/operations/vm-connect/ssh.md#creating-ssh-keys) an SSH key pair on your own.
            * `Load from file`: Upload the public part of the SSH key. You need to create an SSH key pair on your own.
            * `Generate key`: Automatically create an SSH key pair.
            
              When adding a new SSH key, an archive containing the key pair will be created and downloaded. In Linux or macOS-based operating systems, unpack the archive to the `/home/<user_name>/.ssh` directory. In Windows, unpack the archive to the `C:\Users\<user_name>/.ssh` directory. You do not need additionally enter the public key in the management console.
        
        1. Click **Add**.
        
        The system will add the SSH key to your organization user profile. If the organization has [disabled](../../organization/operations/os-login-access.md) the ability for users to add SSH keys to their profiles, the added public SSH key will only be saved in the user profile inside the newly created resource.

  1. Under **General information**, specify the VM name: `test-vm`.
  1. Click **Create VM**.

{% endlist %}

## Connect to the created VM {#connect-to-instance}

When connecting to the VM internal IP address over SSH, you will use your bastion host as a jump host.

To configure SSH access via jump host, add the `-J` (ProxyJump) parameter to the SSH command:

```bash
ssh -i ~/.ssh/<name_of_private_key_file> -J bastion@<public_IP_address_of_bastion_host> test@<internal_IP_address_of_virtual_server>
```

The SSH client will automatically connect to the internal server.

You can use the `-J` flag in OpenSSH version 7.3 or higher. In earlier versions, `-J` is not available. The easiest and most secure alternative is to use standard I/O redirection (the `-W` flag) to forward the connection through the bastion host. Here is an example:

```bash
ssh -i ~/.ssh/<name_of_private_key_file> -o ProxyCommand="ssh -W %h:%p bastion@<public_IP_address_of_bastion_host>" test@<internal_IP_address_of_virtual_server>
```

## Additional connection options {#more-options}

### Using an SSH agent for connection via the bastion host {#using-ssh-agent}

By default, you can only authenticate on the server using a public SSH key. We do not recommend storing keys directly on your bastion host, especially without a passphrase. Use an SSH agent instead. In this case, you will store private SSH keys on your computer only and will be able to safely use them for authentication on the internal server.

To add a key to your authentication agent, use the `ssh-add` command. If the key is stored in the `~/.ssh/id_rsa` file, it is added automatically. You can also select a specific key by running the command below:

```bash
ssh-add <key_file_path>
```

If you use macOS, you can configure the `~/.ssh/config` file and use the following command to add keys to the agent:

```bash
AddKeysToAgent yes
```

The following command connects you to the bastion host and forwards your agent to log in to the internal server using the credentials from your local computer:

```bash
ssh -A bastion@<public_IP_address_of_bastion_host>
```

If you use Windows, add your private keys to the Pageant tool, then open the [PuTTY](https://www.chiark.greenend.org.uk/~sgtatham/putty/) configuration window, select **Connection** → **SSH** → **Authentication**, and configure your agent forwarding.

### Access to services through SSH tunnels {#ssh-tunneling}

Sometimes, SSH access alone is not enough to accomplish your task. In this case, use SSH tunnels allowing you to connect to services requiring inbound connections, e.g., web applications.

There are three main types of SSH tunnels: local, remote, and dynamic:

* A **local** tunnel is an open port on a loopback interface routing your connections to your SSH server `IP:port` address.

   For example, you can connect local port 8080 to the `web_server_IP_address:80` address accessible from your bastion host and then open `http://localhost:8080` in your browser:

   ```bash
   ssh bastion@<public_IP_address_of_bastion_host> -L 8080:<web_server_IP_address>:80
   ```

* A **remote** tunnel works in the direction opposite to that of the local tunnel by opening a local port for incoming connections from a remote server.
* A **dynamic** tunnel is a SOCKS proxy on your local port with connections originating from a remote host. For example, you can set up a dynamic tunnel on port 1080 and then specify it as a SOCKS proxy in your browser. As a result, you will be able to connect to any resources residing in a private subnet and accessible from your bastion host.

   ```bash
   ssh bastion@<public_IP_address_of_bastion_host> -D 1080
   ```

Those techniques are a simpler replacement that in many cases would require VPN connection and can be combined with `ProxyJump` or `ProxyCommand` connections.

If you use Windows, you can set up tunnels in PuTTY by selecting **Connection** → **SSH** → **Tunnels**.

You can use port forwarding, especially a local one, to establish connections to Remote Desktop Services (RDS), i.e., Windows hosts running in a cloud, by tunneling port 3389 and then connecting to `localhost` with an RDS client. If the RDS client is already listening on the local machine, you can use another port as shown in the example below:

```bash
ssh bastion@<public_IP_address_of_bastion_host> -L 3390:<Windows_host_IP_address>:3389
```

### File transfers {#file-transfers}

Linux clients and servers can securely transfer files through the bastion host to internal hosts and back via [SCP](https://en.wikipedia.org/wiki/Secure_copy_protocol) by using `ProxyCommand` and `ProxyJump` options. Here is an example:

```bash
scp -o "ProxyJump bastion@<public_IP_address_of_bastion_host>" <file_name> bastion@<internal_IP_address_of_virtual_server>:<path_to_file>
```

If you use Windows, one of the most popular SCP applications is [WinSCP](https://winscp.net). To transfer files through your bastion host to a remote Linux machine:

1. Create a session with a private host IP address without a password. Configure an SSH key on the Linux machine.
1. In the left-hand navigation menu, click **Advanced** and select **Tunnel**.
1. Specify your bastion host IP address and username. In the **Private key file** field, select the private key you will use to authenticate with your bastion host.
1. In the left-hand navigation menu, under **SSH**, select **Authentication**.
1. Make sure to select **Allow agent forwarding**.
1. Select the private key you will use to authenticate with the private host.

This configuration enables direct file transfer between your Windows machine and Linux private host protected by bastion.

For Windows hosts behind a Linux bastion, you can transfer files by using [RDP](https://en.wikipedia.org/wiki/Remote_Desktop_Protocol) and tunneling. This method ensures an efficient and secure file transfer.

## How to delete the resources you created {#clear-out}

To stop paying for the resources you created:

* [Delete](../../compute/operations/vm-control/vm-delete.md) the VM.
* [Delete](../../vpc/operations/address-delete.md) the static public IP address.