DEV Community

Sophia Parafina
Sophia Parafina

Posted on

Securing Infrastructure: Build a Bastion Host

Protecting your infrastructure begins with controlling access to cloud resources. You can reduce the attack surface area of your infrastructure by creating a single point of entry called either a jump box or a bastion host. These servers provide access to a private network from an external network. However, bastion hosts also present an attack surface and must be configured to harden them from attack. This article demonstrates how to set up a bastion host and configure it to assist with regulatory requirements for your organization.

The Basics

The architecture for implementing a bastion host begins with a server that runs on the public subnet of a virtual private cloud and cloud resources that run on a private subnet that is not publicly accessible. Cloud resources are available through a security group that allows SSH access from the bastion host's security group, as shown in the diagram below.


We'll use AWS for this example, but the same principles apply to other cloud service providers. The first thing to do is to either use a default VPC or create a new VPC. The default AWS VPC has four subnets which are all public, we'll get the id of the first subnet to make it a private subnet for the bastion host.

const vpc = aws.ec2.getVpc({ default: true });
const vpcId = vpc.then(it =>;
const vpcCidrBlock = vpc.then(it => it.cidrBlock);

const subnets = vpc.then(it => aws.ec2.getSubnetIds({ vpcId: }));
const allSubnetIds = subnets.then(it => it.ids);
const firstSubnetId = subnets.then(it => it.ids[0]);
Enter fullscreen mode Exit fullscreen mode

Next, we'll provision the bastion host with an ec2 instance and set up a security group that defines the ingress and egress rules. Note that we can add a public key when we create the bastion, but it must be created beforehand and added to the configuration file.

const sshSg = new aws.ec2.SecurityGroup(`bastion`, {
    vpcId: vpcId,
    ingress: [{ protocol: "tcp", fromPort: 22, toPort: 22, cidrBlocks: [""] }],
    egress: [{ protocol: "-1", fromPort: 0, toPort: 0, cidrBlocks: [""] }],

const sshKey = new aws.ec2.KeyPair("bastion", { publicKey: config.require("publicKey") });

const bastion = new aws.ec2.Instance("bastion", {
    instanceType: aws.ec2.InstanceTypes.T3_Small,
    ami: amiId,
    subnetId: firstSubnetId,
    vpcSecurityGroupIds: [],
    associatePublicIpAddress: true,
    keyName: sshKey.keyName,
Enter fullscreen mode Exit fullscreen mode

And that's it for setting up a basic bastion host. Run pulumi up to start the bastion host.

View Live:

     Type                      Name         Status
 +   pulumi:pulumi:Stack       bastion-dev  created
 +   ├─ aws:ec2:KeyPair        bastion      created
 +   ├─ aws:ec2:SecurityGroup  bastion      created
 +   └─ aws:ec2:Instance       bastion      created

    bastionHost    : ""
    bastionHostName: ""

    + 4 created

Duration: 23
Enter fullscreen mode Exit fullscreen mode

You can ssh securely into your VPC.

$ ssh -i id_rsa
Enter fullscreen mode Exit fullscreen mode

With this basic setup you can add more layers of security such as logging for auditing.

Top comments (0)