Deploying Bastion host(Jump Box) on Google Cloud
By: Avanish Chauhan, Senior Java Developer
Tags: Google, Google Cloud Platform, Bastion Host, Software Development, Luxoft

This is Avanish Chauhan, having 8+ years of experience in Backend technologies like Java, GoLang and RubyOnRails. For the last one and half years, I have been working with Luxoft as Senior Software Developer/Scrum Master. While working with Luxoft, I got an opportunity to work on different tools and technologies, working on Google cloud is one of them.

As I have mentioned in my previous posts that I have started implementing Google Cloud in our project, there are a few challenges that I faced/facing while moving our application from on-premises to Google Cloud platform.

The biggest recent challenge that I faced was to host secure Windows services in a production environment. Our Client was looking for Windows Bastion host or Jump Box which can be accessed externally by the internet, also, I had to make sure that firewall rules are properly configured so that overall connection is secure. This was quite a complex requirement, I had to take care of multiple aspects while implementing this feature, security was foremost important. So, I am going to share in this article how did I drill down the requirements and implemented the required functionality successfully.

The first important point that I would like to mention here is: you have to have good knowledge of software architecture and designing while dealing with such migrations from on-premises to any cloud, so, using my experience and skills of software architecture methodologies, I penned down the main requirement and came up with these sub-tasks which I planned to implement step by step. Instead of jumping directly to the implementation part, I analyzed the pros and cons of every step that I was planning to execute. Here is the list of sub-tasks/steps that I took:

Create Virtual Private Cloud Networks. Create new subnets. Configure a firewall rule, since my requirement was to use Windows Bastion host, so I needed RDP(Remote Desktop Protocol) enabled, so I enabled traffic to TCP port 3389(This is for Windows). Create two instances of Windows server, one of the instances was not having a public IP address. This is to make sure that one of the IP should not be accessible from the internet. Create a Jump Box so that It can be accessed publicly from the internet.

Now let’s see how did I implement this requirement:

I logged into my GCP console and browsed to “VPC Network”, there I created one VPC network (you can give any name while creating a VPC network). Google Cloud gives you the flexibility to create a VPC network using gcloud commands as well, the choice is yours. While creating the VPC network I made sure that It should be “non-default”. Once my non-default VPC network was created with a non-default subnet, the next step was to configure firewall rules properly. This part was really really important and crucial. I browsed to my newly created VPC network and clicked on “firewall rules” and then “add firewall rule”.

PS: These steps can be executed by using cloud shell as well, by using gcloud commands.

We have to take care of a few things while configuring a firewall on the VPC network:

firewall name

target tags

Source IP ranges (I used 0.0.0.0/0)

Select the protocol and ports, since I had to use Windows Bastion host, I selected TCP:3389 Then I created two Virtual Machine Instances, Since our application is having a huge number of users and accessed globally, for the first VM instance I chose 4 vCPUs.

For my case, it was very important to choose proper boot disk, since I had to connect to Windows Bastion host I selected “Windows Server 2016 Datacenter” and size I gave as 250GB. Then to fulfil the client’s requirements I modified the network interfaces of my VM instance, I passed the following values in the network interface:

Network: One of my already created VPC network(non-default)

Subnetwork: My already created subnet(non-default)

Primary Internal IP: Ephemeral

External IP: none (As Client didn’t want it to be exposed to the external world or we can say the internet)

I added one more network interface to my VM instance by passing the following values:

Network: Default

Subnetwork: Default

Primary Internal IP: Ephemeral

External IP: none (As Client didn’t want it to be exposed to the external world or we can say the internet)

For the second VM instance, most of the configurations were similar to the first one except for the network interfaces configurations:

Network: One of my already created VPC network(non-default)

Subnetwork: My already created subnet(non-default)

Primary Internal IP: Ephemeral

External IP: Ephemeral

********************************************

Network: Default

Subnetwork: Default

Primary Internal IP: Ephemeral

External IP: none (As Client didn’t want it to be exposed to the external world or we can say the internet)

Note: All the above-mentioned steps could also be executed by using cloud shell(gcloud commands).

So now I was having two VM instances one with no external IP and the second one having one external IP, both of those were having RDP protocol configured.

Since I had to connect one of my VM instances to Bastion Host (Jump Box), I reset my password using the following command on cloud shell:

gcloud compute reset-windows-password <VM Instance name having external IP> — user admin — zone us-central1-d Then I browsed to my the VM instance having external IP, I clicked on the “connect” option and then using dropdown I tried to connect using RDP(Remote Desktop Protocol).

From my machine, I did “mstsc” (Remote Desktop Connection), In Computer Address section I passed the external IP of my VM instance from GCP console. User name as “admin’ and password as my recently set password. But when I clicked on the connect button, oops, I got “connection refused” error, so I was totally clueless what went wrong.

I started analysing the issue to find the root cause, I followed the bottom-up approach. First I opened my VM instance and checked each and every detail:

Is HTTP access enabled? Yes

Is proper Windows server selected? Yes

Are network interfaces configured properly? Yes

Have you configured network tag properly? No, my bad :(

So, I found that I missed adding the proper network tag to my VM instance, but, how to find which network add I forgot to add? I browsed to the created VPC networks and check the firewall name that I configured there. I added that name in the network tag field of my VM instance and BOOM, it was a success.

Note: Add the same network tag to your VM instance that you have created while configuring firewall rules, it will make sure that you don’t get any connection refused error while connecting to your VM using remote desktop connection option from your machine. So, finally, I could access my VM instance from my machine by using an external IP of my VM instance. Now I was inside one of my VM instances which were having external IP exposed. Client’s requirement was to access the second VM instance from that first VM instance. Please remember that the second VM instance was not having external IP exposed. I did “mstsc” inside that remotely connected VM instance, but before that, I reset the password for second VM instance. Once the password reset is done, I did “mstsc” from the first VM instance by passing following details:

Address: internal IP of second VM instance:

Username: <the value that I set for the second VM>

Password: <the value that I set for the second VM> Once it is done, I installed one IIS server inside my second VM instance which was connected remotely through Remote Desktop Connection protocol. I did some customizations as per the client’s requirement while installing IIS server like the client wanted .Net framework etc. After this installation was completed, I could access my IIS server by hitting the external IP of the first VM instance in my internet browser. So, in the end, my jump box was looking like this:

Client Machine → First VM Instance(With External IP) → Second VM Instance(Only Internal IP) → IIS server.

I faced multiple issues while implementing this functionality and achieving the desired results, but I didn’t quit, each failure taught me something new. Whenever I had any issue, I debugged it precisely, went to the root of the issue and fixed it.