The intricacies of running containers on OpenShift

In the context of the ECRP Project,  which is part of our cloud robotics initiative, we are aiming to build a PaaS solution for robotic applications.

The “Robot Operating System” (ROS) is widely used on several robotics platforms, and also runs on the turtlebot robots in our lab. One of the ideas behind cloud robotics is to enable ROS components (so called ROS nodes) to run distributed across the cloud infrastructure and the robot itself, so we can shift certain parts of the robotics application to the cloud. As a logical first step we tried to run existing ROS nodes, such as a ROS master in containers on Kubernetes, then we tried to use a proper Platform as a Service (PaaS) solution, in our case Red Hat OpenShift .

OpenShift offers a full PaaS experience, you can build and run code from source or run pre-built containers directly. All of those features can be managed via a intuitive web interface.

However, OpenShift imposes tight security restrictions on the containers it runs.
These are:

  • Prevention from running processes in containers as root
  • Using random user ID for running containers (Support Arbitrary User IDs)

As a cluster admin you are able to specify and manage the security context constraints, which allow you to specify granular security constraints for tenants. With such rules an administrator is able to specify which users are allowed to run containers under specific  constraints. Therefore the strict security restriction can be relaxed if needed. We focus here on a standard configuration, in which those security restrictions are fully enabled and we act as normal user which has no special roles and permissions assigned.

For ROS containers the prevention of running processes as root is not a big issue, because ROS components are intended not to require root permissions in general. Nevertheless by default the existing ROS containers run all their processes as root.

So our first step in adapting a generic container from Docker Hub to run on OpenShift was to repackage the container and switch to a non root user leveraging the USER statement inside the Dockerfile.

FROM ros:indigo
# install
RUN apt-get update && apt-get install -y ros-indigo-move-base-msgs

# setup entrypoint
ADD ./ros_entrypoint.sh /

# add user
RUN adduser --disabled-password rosuser
USER rosuser

ENTRYPOINT ["/ros_entrypoint.sh"]
CMD ["/bin/bash", "-c","/ros_entrypoint.sh roscore"]

However, this is not enough. The container still does not run properly and crashes shortly after starting it. This is due to the fact that OpenShift uses a random user ID at runtime to run containers. In our case, the ROS master node tries to write its log files to a directory owned by the user we defined in the Dockerfile, however the userid of the user that is running the container and the userid that we used for setting up the container differ, hence the user at runtime has insufficient permissions to write on the Dockerfile user directories. This is a know issue, and a detailed treatment of the problem and how to solve it can be found in this blog post from Graham Dumpleton: http://blog.dscpl.com.au/2015/12/random-user-ids-when-running-docker.html
In this blogpost we just give a quick solution that works.

One way to fix the permission problem would be to give write access to the world to the logging directory. We don’t really like this solution and it would also be slightly odd if the result of a focus on security was the requirement of opening up permissions to everybody.

The other way to go are group permissions. But we also can’t just create a new group and add the user to it, because the random user which is used at runtime can not be added to this group before. So the trick is to look at the standard group of the random user. If we run the “id” command we can see that this random user is always in the group “root” with the id 0.

The solution is therefore to create a user which has also the group “root” as standard group defined. This way we can ensure that every directory which is created by that user is also writable by the random user at runtime.

The modified Dockerfile would look something like this:

# add user and make sure her stuff is writable 
# whichever userid is given at runtime
RUN adduser --disabled-password --gid 0 --gecos "ROS user" rosuser
USER rosuser
RUN chown rosuser:root /home/rosuser && chmod 0775 /home/rosuser
ENV HOME /home/rosuser

The required steps are:

  • Add a user with the gid of 0, which is the “root” group
  • Switch to the newly created user
  • Change the ownership and the permission of the home directory to the current user and “root” group.
  • Set the HOME environment variable to the current home dir of the user. This way also the random user has his home set to the same directory

With these changes we were able to run ROS containers on OpenShift. Also any other container which was firstly designed to run as root, can be modified this way to run on OpenShift. The downside is, you still need to manually change the ownership and permissions on every directory you will need at runtime.

Leave a Reply

Your email address will not be published. Required fields are marked *