I have been contributing to CentOS Container Pipeline for over a year now, and as part of my contributions, working to get containers built into pipeline, where they can be tested, scanned, and lifecycled, and made them available on registry.centos.org,  being used to host containers, approved by CentOS Community, for the CentOS Community.

We picked #moodle to be one of the containers that we would bootstrapped onto the pipeline.

One of the requirements, is to ensure that we can as many of these containers we get, to be functional with #OpenShift.

For those not aware of it, #openshift, and of course #kubernetes, don’t run containers as root. In fact, each time a container runs, it is run with a new random user id by the #openshift. This ensures that a the user inside the container is never root, and in fact, you cant predict the user easily, granting greater security.

However, whatever the userid is, the user will always be a part of the root group, or gid 0. This means that when the container is run on #openshift, unless the permissions have been setup correctly, you will get a permission denied error, which any of you, who have tried building and running containers on openshift would have already encountered.

This means that when a container runs on openshift, any file that it accesses, should have proper permissions set, on the root group.

$ ls -l index.php 
-rw-rw-r--. 1 apache root 13533 xxx xx xxxx index.php

This should be taken care of even during container builds as not all  files may be shared between containers, not every will will mount something with appropriate permissions, every time he runs a container.

An easy way to achieve this is to have a script / function that looks something like

set -eux
find "$1" -exec chown ${2} {} \;
find "$1" -exec chgrp 0 {} \;
find "$1" -exec chmod g+rw {} \;
find "$1" -type d -exec chmod g+x {} +

And then, towards the end of the installation process, either in you install script, or in dockerfile, run this over every path that will require the permission fix.

for item in "/somepath1" "/somepath2" "/somepath3"; do
    . /opt/scripts/fix-permissions.sh ${item} somenonrootuser;
done

Assuming of course that you had a ADD ./fix-permissions.sh /opt/scripts somewhere. For most containers, this will usually be enough.

However, since #moodle is a php application, that requires a web server, like Apache, another issue crops up, in that, Apache, and some other software, such as postgresql, nginx etc, need the user who runs it, to have a specific name.

So to overcome this, I make use of Name Service Switch or nss wrapper for short. I make use of this here to spoof the username, so that even though the user id changes, if some queries for who they are, they will get the answer of the user they want to see.

The way we get this to work is first, we create a passwd template file, which looks something like

root:x:0:0:root:/root:/bin/bash
bin:x:1:1:bin:/bin:/sbin/nologin
daemon:x:2:2:daemon:/sbin:/sbin/nologin
adm:x:3:4:adm:/var/adm:/sbin/nologin
lp:x:4:7:lp:/var/spool/lpd:/sbin/nologin
sync:x:5:0:sync:/sbin:/bin/sync
shutdown:x:6:0:shutdown:/sbin:/sbin/shutdown
halt:x:7:0:halt:/sbin:/sbin/halt
mail:x:8:12:mail:/var/spool/mail:/sbin/nologin
operator:x:11:0:operator:/root:/sbin/nologin
games:x:12:100:games:/usr/games:/sbin/nologin
ftp:x:14:50:FTP User:/var/ftp:/sbin/nologin
nobody:x:99:99:Nobody:/:/sbin/nologin
apache:x:${USER_ID}:${GROUP_ID}:Apache User:${HOME}:/bin/bash

Of course, the container must install a couple of packages to make use of nss magic, and that will be nss_wrapper (for nss duh!) and gettext (for envsubst), and a few of you will already be kind of seeing where this is going.

Once we have this, whenever the container is run, we basically need to do two things:

  1. Find out the current uid and gid, users home and replace the values in the passwd template.
  2. Make a fool of the system so he thinks the passwd file we got from previous step is the passwd file.

These 2 steps can be achieved by doing the following in the containers entry point:

export USER_ID=$(id -u);
export GROUP_ID=$(id -g);
envsubst < /opt/scripts/passwd.template > /tmp/passwd;
export LD_PRELOAD=libnss_wrapper.so;
export NSS_WRAPPER_PASSWD=/tmp/passwd;
export NSS_WRAPPER_GROUP=/etc/group;

And that is how i got the moodle container working on openshift. You can see the full source, of course at https://github.com/CentOS/CentOS-Dockerfiles/pull/130 and should be on CentOS Dockerfiles soon.

And as far as how to get it to run on openshift, well you could try this basic template:

Note: This container will be available in registry.centos.org/centos/moodle:3.1 soon.

apiVersion: v1
items:
- apiVersion: v1
 kind: Service
 metadata:
 creationTimestamp: null
 labels:
 service: moodle
 name: moodle
 spec:
 ports:
 - name: "8080"
 port: 8080
 targetPort: 8080
 - name: "8443"
 port: 8443
 targetPort: 8443
 selector:
 service: moodle
 status:
 loadBalancer: {}
- apiVersion: v1
 kind: Service
 metadata:
 creationTimestamp: null
 labels:
 service: postgres
 name: postgres
 spec:
 clusterIP: None
 ports:
 - name: headless
 port: 55555
 targetPort: 0
 selector:
 service: postgres
 status:
 loadBalancer: {}
- apiVersion: v1
 kind: DeploymentConfig
 metadata:
 creationTimestamp: null
 labels:
 service: moodle
 name: moodle
 spec:
 replicas: 1
 selector:
 service: moodle
 strategy:
 resources: {}
 template:
 metadata:
 creationTimestamp: null
 labels:
 service: moodle
 spec:
 containers:
 - env:
 - name: DB_HOST
 value: postgres
 - name: DB_TYPE
 value: pgsql
 image: ' '
 name: moodle
 ports:
 - containerPort: 8080
 - containerPort: 8443
 resources: {}
 restartPolicy: Always
 test: false
 triggers:
 - type: ConfigChange
 - imageChangeParams:
 automatic: true
 containerNames:
 - moodle
 from:
 kind: ImageStreamTag
 name: moodle:latest
 type: ImageChange
 status: {}
- apiVersion: v1
 kind: ImageStream
 metadata:
 creationTimestamp: null
 name: moodle
 spec:
 tags:
 - annotations: null
 from:
 kind: DockerImage
 name: mohammedzee1000/moodle
 generation: null
 importPolicy: {}
 name: latest
 status:
 dockerImageRepository: ""
- apiVersion: v1
 kind: DeploymentConfig
 metadata:
 creationTimestamp: null
 labels:
 service: postgres
 name: postgres
 spec:
 replicas: 1
 selector:
 service: postgres
 strategy:
 resources: {}
 template:
 metadata:
 creationTimestamp: null
 labels:
 service: postgres
 spec:
 containers:
 - env:
 - name: POSTGRESQL_DATABASE
 value: moodle
 - name: POSTGRESQL_PASSWORD
 value: moodle
 - name: POSTGRESQL_USER
 value: moodle
 image: ' '
 name: postgres
 resources: {}
 restartPolicy: Always
 test: false
 triggers:
 - type: ConfigChange
 - imageChangeParams:
 automatic: true
 containerNames:
 - postgres
 from:
 kind: ImageStreamTag
 name: postgres:9.6
 type: ImageChange
 status: {}
- apiVersion: v1
 kind: ImageStream
 metadata:
 creationTimestamp: null
 name: postgres
 spec:
 tags:
 - annotations: null
 from:
 kind: DockerImage
 name: registry.centos.org/postgresql/postgresql:9.6
 generation: null
 importPolicy: {}
 name: "9.6"
 status:
 dockerImageRepository: ""
kind: List
metadata: {}

That’s all folks

Advertisements

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s