The goal of this project is to let go of old-fashioned virtual machines and transition to Docker containers to cut infrastructure costs. We will be offering infrastructure as a service (IaaS) in the form of extremely cheap and tiny servers. Each of these servers is powered by an underlying complete virtual machine. The clients have minimal use cases, and that's why they like the affordable proposal of small servers. Access to these servers is provided via SSH to the clients that provide a new VM. To create the servers, we will be writing a script that allows users to customize their SSH access.
We will implement a solution that will allow customers to provision servers the way they usually do, and they will still have SSH access as usual, but the major key difference is that a Docker container will be created instead of a VM.
To make this happen, we will first have to figure out a way to spin up a Docker container that allows access via SSH with username and password as well as SSH keys using a single Dockerfile. Then, once the Dockerfile describing all the SSH installation and configuration is created, we will integrate this with our bash script. This is the script our clients run to provision servers. We will consider this implementation successful when a client runs the script and the following happens under the hood:
a) SSH keys are generated.
b) A Docker container is created with the SSH keys that were generated.
c) The same Docker container allows login with the username and password provided by the client.
Let us start with the basics. We will first create a Dockerfile for SSH containers.
If you are new to creating and building Docker files and Docker images, I would highly recommend you read the following blog post:
https://devopscube.com/build-docker-image/
Create a Dockerfile; we can use Ubuntu as our first layer on which to build our docker image, which will include an ssh server and allow users remote access.
Add the following code to your Dockerfile:
The code begins by downloading an Ubuntu image.
The code then runs the update command and installs the OpenSSH-server package.
Next, it creates a directory called /run/sshd and then runs the sshd command with the -D option, which makes it run in daemon mode.
The purpose of this code is to allow SSH access to a remote machine.
Now let us build the Docker image from this Docker file.
docker build -t ssh-image .
This is the tag flag here; after building the image, it will show the output of creating layers. It really feels great watching this XD.
After building the image, we will try to run the container to check if the image was created properly.
docker run -d --name ssh-container ssh-image
Now try to list the running containers via the following command:
docker ps -a
If you run into any problems and want to figure out what is wrong, run the following command:
docker logs -f ssh-container
Now our main goal is to create Docker containers that will give ssh access through username, password, or ssh keys.
First, let's try to figure out a way to get the username and password defined in the Docker file.
As we are building the logic, let's first add a user with a dummy username and password through environment variables and then add the user directory. Modify the Docker file as follows.
FROM ubuntu:latest
RUN apt-get update && apt-get install -y openssh-server iproute2
RUN mkdir /var/run/sshd
# Add the user with the desired ssh credentials
RUN useradd -m -s /bin/bash testuser
RUN echo "testuser:testpassword" | chpasswd
EXPOSE 22
CMD ["/usr/sbin/sshd", "-D"]
Remember to stop the running container by command and remove the image initially created
docker rm -fv $(docker ps -aq)
Repeat the previous steps: build the image, then run the container with this new image.
docker run -d -p 2222:22 my-ssh-image
With this, we can say that we can create a Docker container for users who can give a user ID and password to access the container once it is provisioned.
Now let us try to generate a dynamic username and password.
For this, write a simple script that prompts you to add a username and password, and this will configure the docker file such that it will create a user who can use his/her password to directly ssh into the container.
#!/bin/bash
set -e
### Collect user and password info
read -p 'Username: ' username
read -sp 'Password: ' password
if [ -z "${username}" ] || [ -z "${password}" ]; then
echo
echo "Username or Password can't be empty. Please, try again."
exit 1
fi
echo
### 1. Configure the ssh server with the given user and password.
# Do the config for user and password here ...
build_args=" --build-arg SSH_USER=$username --build-arg SSH_PASSWORD=$password"
docker build -t ssh-image . $build_args
docker rm -fv ssh-container|| true
# Create the container to test the changes
docker run -d -p 22:22 -p 80:80 --name ssh-container ssh-image
#### Change the <> placeholders to point to actual values
ip=$(docker inspect --format '{{.NetworkSettings.IPAddress}}' ssh-container)
echo "The server is ready and accessible at $ip, with user $username and the password that you set".
Next, we must modify the Dockerfile because it will use the script input as username and password; change the Dockerfile as follows:
FROM ubuntu:latest
RUN apt-get update && apt-get install -y openssh-server iproute2
RUN mkdir /var/run/sshd
ARG SSH_USER
ARG SSH_PASSWORD
# Add the user with the desired ssh credentials
RUN useradd -m -s /bin/bash $SSH_USER
RUN echo "$SSH_USER:$SSH_PASSWORD" | chpasswd
EXPOSE 22 80
CMD ["/usr/sbin/sshd", "-D"]
Now make the bash script executable by
chmod +x generate_server.sh
This will prompt the user for a username and password; after this, it will build the Docker image and run the container.
Now the user has to just access the Docker container via SSH.
ssh username@localhost
At this point, the user is not able to connect with an IP address but can connect through localhost. If anyone figures this out, kindly help me through this.
Now we can say that we are halfway through our project. Next, we also want our users to log in without a password, which is done through a private key. This will be easy, as we already know that we have to make a few changes in the script and Dockerfile.
Make changes to the script file such that it uses ssh-keygen to create keys.
#!/bin/bash
set -e
### Collect user and password info
read -p 'Username: ' username
read -sp 'Password: ' password
if [ -z "${username}" ] || [ -z "${password}" ]; then
echo
echo "Username or Password can't be empty. Please, try again."
exit 1
fi
echo
### 1. Configure the ssh server with the given user and password.
build_args=" --build-arg SSH_USER=$username --build-arg SSH_PASSWORD=$password"
### 2. Generate ssh keys for this user and allow them to login using the generated key
echo "Generating ssh keys for user $username ..."
ssh-keygen -q -f $username -N ""
chmod 700 $username
build_args+=" --build-arg SSH_PUB_KEY_PATH=${username}.pub"
docker build -t ssh-image . $build_args
docker rm -fv ssh-container|| true
# Create the container to test the changes
docker run -d -p 22:22 -p 80:80 --name ssh-container ssh-image
#### Change the <> placeholders to point to actual values
ip=$(docker inspect --format '{{.NetworkSettings.IPAddress}}' ssh-container)
echo "The server is ready and accessible at $ip, with user $username and the password that you set".
echo "You can find your ssh private key at $PWD/$username"
Now we have to add the public key as an argument in the Dockerfile so that users can access the container through the private key.
FROM ubuntu:latest
RUN apt-get update && apt-get install -y openssh-server iproute2
RUN mkdir /var/run/sshd
ARG SSH_USER
ARG SSH_PASSWORD
# Add the user with the desired ssh credentials
RUN useradd -m -s /bin/bash $SSH_USER
RUN echo "$SSH_USER:$SSH_PASSWORD" | chpasswd
ARG SSH_PUB_KEY_PATH
COPY $SSH_PUB_KEY_PATH /home/$SSH_USER/.ssh/authorized_keys
EXPOSE 22 80
CMD ["/usr/sbin/sshd", "-D"]
Finally, let us run the script to test all the changes
b is the private key generated here; the private key is named the same as the username that was provided.
To ssh inside the container through our private key, we have to run the following command:
Here, b is the private key, and b in b@localhost is the username.
We can now say that we have successfully built an Iaas using docker, which helps clients to get servers as docker containers so that they can access these through ssh username and password or keys without the user's knowledge that they are using docker containers.
So instead of using virtual machines to provide services to clients, we can use Docker containers, which are lightweight compared to virtual machines.
Visit my github profile for the code :)