Writing a multi-language online compiler for VS Code Part 3: Docker and Free Hosting

Ben Meehan
6 min readJun 17, 2023
Photo by Rubaitul Azad on Unsplash

In the Last Part, we wrote the Golang service that will compile the code and give us the output through an API request.

In this part, we will take the Golang code that we wrote and dockerize it. We will also go over how to set the permissions inside the docker container to restrict what the code inside it can do. We will also take a look at how we can host this Docker container for absolutely free.

Before we go to docker, I want to go over the concept of Jailing in Operating Systems in a bit more detail.

What exactly are we trying to solve here?

When we run some code that the user sends inside our server, we want to ensure that they do not end up messing with our file system or executing unwanted commands.

There are many ways to prevent this. But the two most common are Sandboxing and Jailing. We will be using Jailing here.

What is Jailing?

Jailing is the concept of restricting the file system access to certain programs. So as to as that the program is ‘jailed’ within the folders it has access to.

Remember in Unix everything is a file or a folder at the end of the day. So, if we say only ‘the root user’ can access the files inside /system/bin, that means any other users on the OS cannot execute any of the binaries inside /system/bin like the ‘ls’ or ‘cd’ command.

Other operating systems like Windows also have similar file/directory permissions.

Dockerizing the C++ Compilation Service

To start with go ahead and create a Dockerfile in the same directory as the go file we worked on in the last part.

The folder structure should look something like this

|--- CPP  
|--- main.go
|--- Dockerfile

Let’s start by setting the latest distribution of Golang as the base image and creating a /app working directory inside the docker container

FROM golang:latest

WORKDIR /app

The default image for Golang will use Ubuntu. You can also start with a Ubuntu base image and then install golang manually.

/app is the place where all our server code and go.mod files will be stored.

Next, let’s install the libraries and compilers needed to compile the C++ code.

# Install required libraries for C++ compilation
RUN apt-get update && \
apt-get install -y build-essential && \
apt-get install -y libstdc++6

This will change depending upon the programming language so make sure to do your own research on what are the dependencies needed for your language to compile and run.

Now, let’s copy the main.go file into the docker container

COPY . .

Next, come the permissions. By default, Golang creates a restricted user with very limited permissions and with the user and group ids of 1000. All the go programs inside the container are run by this user.

We are going to take advantage of this and allow or grant access to folders for this restricted user and group as we please.

Quick Crash Course on Linux Permissions

If you do ls -l on your Unix-based system you will see something like this,

drwxr-xr-x 4 ben.meehan  Users     128 Mar 27 17:43 test

The first letter ‘d’ specifies if it is a file or a folder.

The first 3 ‘rwx’ specify the read, write and execute permission for the owner of the file.

The next 3 ‘rwx’ specify the read, write and execute permission for the group of files.

The next 3 ‘rwx’ specify the read, write and execute permission for all the other users of the system.

The restricted user created by Golang is treated as an ‘other user’. So, we are basically going to play around with the permissions of the group and others.

# Set permissions for directories excluding /lib, /lib64, and /tmp
RUN chmod go-wrx /app /bin /boot /dev /etc /go /home /media /mnt /opt /root /run /sbin /srv /usr /var

# Set permissions for /lib and /lib64
RUN chmod go+x-wr /lib /lib64 /usr

# Set permissions for /tmp
RUN chmod go+x-wr /tmp

# C++ expects shared libs in /usr/lib. Remove all other permissions
RUN chmod go-x /usr/bin /usr/games /usr/include /usr/libexec /usr/local /usr/sbin /usr/share /usr/src

The chmod go-wrx removes read, write and execute permissions for all the folders mentioned for all groups and other users.

Initially we remove all the permissions for every folder except the /tmp and the library folders /lib, /lib64 and /usr/lib.

This is necessary as the system libraries needed by the Golang server and g++ compiler are present in /lib. The compiled machine code needs to be stored and run from/tmp.

Finally, we remove all the permissions for the specific folders that we don’t need.

This is going to limit what the C++ code can do and what system commands the user can execute.

Okay, Now the only thing that’s left is to compile our main.go file inside the container and run it.

# Initialize Go modules
RUN go mod init github.com/benmeehan/code-online/cpp-compiler

# Get the necessary go modules
RUN go mod tidy

# Compile the go code
RUN go build -o app .

# Expose PORT 8080
EXPOSE 8080

# Run the compiled code
CMD ["./app"]

Since our Golang server listens on port 8080, we also need to expose that.

Thats it! That’s our Dockerfile. You can build and run it using the commands

Note: Make sure you have the docker engine installed on your PC.

docker build -t cpp-compiler .
docker run -p 8080:8080 cpp-compiler

Now, you can send a POST request to http://localhost:8080/compile with your C++ code to get the output.

Hosting the Docker

The problem for me was that I didn’t want to run this docker locally on my computer. I wanted to host it somewhere so that I can access it from any of my devices.

This is where I found a wonderful website called Render.com. It is kind of like the OG Heroku.com. They offer a free 100 m CPU and 512 MB of RAM to host all your static and web services. No Credit Card Required.

To host your docker, follow these steps.

  1. Create a free account at Render
  2. Push your Dockerfile to a repo in Github/Gitlab
  3. Link your Github/Gitlab account to Render.com
  4. Click on +NEW at the top menu bar and select WEB SERVICE
  5. Select the Github/Gitlab Repo with your code
  6. Give the name and root directory where the Dockerfile is present
  7. Click Deploy

It will take a while, but once done you will get a URL which you can make a request to compile your code.

We did the deployment for only C++. But you can follow the same process and modify the code in main.go and Dockefile to deploy any other language compiler. Check out this repo for samples.

As you can guess if we have more than 1 language, we will have more than 1 URL. In the next part of this tutorial, We will write a very simple load balancer that will give us a single URL for all the languages.

--

--

Ben Meehan

Software Engineer at Razorpay. Sharing knowledge and experiences.