The document discusses OpenShift security context constraints (SCCs) and how to configure them to allow running a WordPress container. It begins with an overview of SCCs and their purpose in OpenShift for controlling permissions for pods. It then describes issues running the WordPress container under the default "restricted" SCC due to permission errors. The document explores editing the "restricted" SCC and removing capabilities and user restrictions to address the errors. Alternatively, it notes the "anyuid" SCC can be used which is more permissive and standard for allowing the WordPress container to run successfully.
3. What is OpenShift?
● Platform as a Service
● Open Source
● Container based
● Kubernetes based
● Development oriented
● Multitenant
● Web-based interface
● Integrated Registries
● HA configurable
● Integrated metrics
● git lover
★ You care about service and its technologies
★ Github hosted code
★ Docker and more
★ Do you know Google? :)
★ Automated builds and much more
★ Many users, grants, policies and isolation
★ Who cares? :)
★ Who needs internet? Local Registries!
★ Set the replicas and Origin will care
★ Do you like graphs?
★ Yeah, we love git!
4. All cool but..
Where is the start button?
$ oc cluster up
-- Checking OpenShift client ... OK
-- Checking Docker client ... OK
-- Checking Docker version ... OK
-- Checking for existing OpenShift container ... OK
-- Checking for openshift/origin:v1.3.1 image ... OK
-- Checking Docker daemon configuration ... OK
-- Checking for available ports ... OK
-- Checking type of volume mount ...
Using nsenter mounter for OpenShift volumes
-- Creating host directories ... OK
-- Finding server IP ...
Using public hostname IP 192.168.123.1 as the host IP
Using 192.168.123.1 as the server IP
-- Starting OpenShift container ...
Starting OpenShift using container 'origin'
Waiting for API server to start listening
OpenShift server started
-- Installing registry ... OK
-- Installing router ... OK
-- Importing image streams ... OK
-- Importing templates ... OK
-- Login to server ... OK
-- Creating initial project "myproject" ...
Now using project "myproject" on server
"https://192.168.123.1:8443".
-- Server Information ...
OpenShift server started.
The server is accessible via web console at:
https://192.168.123.1:8443
5. What are Security Context Constraints?
● OpenShift gives its administrators
the ability to manage a set of
security context constraints
(SCCs) for limiting and securing
their cluster.
● Security context constraints allow
administrators to control
permissions for pods.
SCCs allow an administrator:
1. Run privileged containers.
2. Set capabilities a container can request.
3. Use of host directories as volumes.
4. Set SELinux context of the container.
5. Set the user ID for the container.
6. The use of host namespaces and networking.
7. Define ‘FSGroup’ for the pod’s volumes
8. Configure allowable supplemental groups
9. Require the use of a read only file system
10. Control the usage of volume types
A pod is the smallest
OpenShift’s compute unit.
It’s one or more containers)
deployed together on one
host.
8. Setting up
prerequisites for the wordpress
container
The following environment variables are also honored
for configuring your WordPress instance:
-e WORDPRESS_DB_HOST=... (defaults to the IP and
port of the linked mysql container)
-e WORDPRESS_DB_USER=... (defaults to "root")
-e WORDPRESS_DB_PASSWORD=...
$ oc new-app mariadb -e MYSQL_ROOT_PASSWORD=mysecret
--> Found image 1dc122b (3 weeks old) in image stream "mariadb" in
project "openshift" under tag "10.1" for "mariadb"
MariaDB 10.1
------------
MariaDB is a multi-user, multi-threaded SQL database server
Tags: database, mysql, mariadb, mariadb101, rh-mariadb101,
galera
* This image will be deployed in deployment config "mariadb"
* Port 3306/tcp will be load balanced by service "mariadb"
* Other containers can access this service through the hostname
"mariadb"
* This image declares volumes and will default to use
non-persistent, host-local storage.
You can add persistent volumes later by running 'volume
dc/mariadb --add ...'
...
9. Stop talking: run my container!
$ oc new-app wordpress -e WORDPRESS_DB_HOST=mariadb -e WORDPRESS_DB_USER=root -e WORDPRESS_DB_PASSWORD=mysecret
--> Found Docker image ed69ee3 (6 days old) from Docker Hub for "wordpress"
* An image stream will be created as "wordpress:latest" that will track this image
* This image will be deployed in deployment config "wordpress"
* Port 80/tcp will be load balanced by service "wordpress"
* Other containers can access this service through the hostname "wordpress"
* This image declares volumes and will default to use non-persistent, host-local storage.
You can add persistent volumes later by running 'volume dc/wordpress --add ...'
* WARNING: Image "wordpress" runs as the 'root' user which may not be permitted by your cluster administrator
--> Creating resources with label app=wordpress
...
$ oc get pods
NAME READY STATUS RESTARTS AGE
wordpress-1-deploy 1/1 Running 0 17s
wordpress-1-072ui 0/1 Error 0 14s
$ oc logs wordpress-1-072ui
AH00558: apache2: Could not reliably determine the server's fully qualified domain name, using 172.17.0.7. Set the
'ServerName' directive globally to suppress this message
(13)Permission denied: AH00072: make_sock: could not bind to address [::]:80
(13)Permission denied: AH00072: make_sock: could not bind to address 0.0.0.0:80
no listening sockets available, shutting down
AH00015: Unable to open logs
10. Ok, fun but..
What happened to my container?
$ oc describe pod/wordpress-1-072ui | head
Name: wordpress-1-072ui
Namespace: myproject
Security Policy: restricted
Node: 192.168.123.1/192.168.123.1
Start Time: Thu, 12 Jan 2017 12:47:04
+0100
Labels: app=wordpress
deployment=wordpress-1
deploymentconfig=wordpress
Status: Running
IP: 172.17.0.7
$ oc debug wordpress-1-072ui
Debugging with pod/wordpress-1-072ui-debug,
original command: docker-entrypoint.sh
apache2-foreground
Waiting for pod to start ...
Pod IP: 172.17.0.4
If you don't see a command prompt, try pressing
enter.
$ id
uid=1000040000 gid=0(root)
groups=0(root),1000040000
11. Solutions
For your lovely container
● Edit the ‘restricted’ SCC
OR
● Use the ‘anyuid’ SCC
OR
● Rebuild through Dockerfile
14. Inspecting the ‘restricted’ SCC
$ oc get scc/restricted -o yaml
allowHostDirVolumePlugin: false
allowHostIPC: false
allowHostNetwork: false
allowHostPID: false
allowHostPorts: false
allowPrivilegedContainer: false
allowedCapabilities: null
apiVersion: v1
defaultAddCapabilities: null
fsGroup:
type: MustRunAs
groups:
- system:authenticated
kind: SecurityContextConstraints
metadata:
annotations:
kubernetes.io/description: restricted denies
access to all host features and requires
pods to be run with a UID, and SELinux context
that are allocated to the namespace. This
is the most restrictive SCC.
creationTimestamp: 2016-12-22T10:04:27Z
name: restricted
resourceVersion: "102"
selfLink:
/api/v1/securitycontextconstraints/restricted
uid: 05f68498-c82e-11e6-b2bd-68f7286606f4
priority: null
readOnlyRootFilesystem: false
requiredDropCapabilities:
- KILL
- MKNOD
- SYS_CHROOT
- SETUID
- SETGID
runAsUser:
type: MustRunAsRange
seLinuxContext:
type: MustRunAs
supplementalGroups:
type: RunAsAny
volumes:
- configMap
- downwardAPI
- emptyDir
- persistentVolumeClaim
- secret
15. Editing the ‘restricted’ SCC
$ oc get scc restricted -o yaml | grep runAsUser -A1
runAsUser:
type: MustRunAsRange
$ oc edit scc restricted
securitycontextconstraints "restricted" edited
$ oc get scc restricted -o yaml | grep runAsUser -A1
runAsUser:
type: RunAsAny
$ oc get pod
NAME READY STATUSRESTARTS AGE
mariadb-1-l4ycb 1/1 Running 0 2h
wordpress-1-ai3gj 1/1 Running 0 52s
16. Take a look to container’s logs
$ oc logs wordpress-1-ai3gj
WordPress not found in /var/www/html - copying now...
Complete! WordPress has been successfully copied to /var/www/html
AH00558: apache2: Could not reliably determine the server's fully qualified domain name, using 172.17.0.4. Set
the 'ServerName' directive globally to suppress this message
AH00558: apache2: Could not reliably determine the server's fully qualified domain name, using 172.17.0.4. Set
the 'ServerName' directive globally to suppress this message
[Thu Jan 12 13:47:46.849951 2017] [unixd:alert] [pid 170] (1)Operation not permitted: AH02156: setgid:
unable to set group id to Group 33
[Thu Jan 12 13:47:46.850406 2017] [unixd:alert] [pid 171] (1)Operation not permitted: AH02156: setgid:
unable to set group id to Group 33
[Thu Jan 12 13:47:46.850735 2017] [unixd:alert] [pid 172] (1)Operation not permitted: AH02156: setgid:
unable to set group id to Group 33
[Thu Jan 12 13:47:46.851119 2017] [unixd:alert] [pid 173] (1)Operation not permitted: AH02156: setgid:
unable to set group id to Group 33
[Thu Jan 12 13:47:46.851398 2017] [mpm_prefork:notice] [pid 1] AH00163: Apache/2.4.10 (Debian) PHP/5.6.29
configured -- resuming normal operations
[Thu Jan 12 13:47:46.851458 2017] [core:notice] [pid 1] AH00094: Command line: 'apache2 -D FOREGROUND'
[Thu Jan 12 13:47:46.851500 2017] [unixd:alert] [pid 174] (1)Operation not permitted: AH02156: setgid:
unable to set group id to Group 33
17. A step back to the ‘restricted’ SCC
$ oc get scc/restricted -o yaml
allowHostDirVolumePlugin: false
allowHostIPC: false
allowHostNetwork: false
allowHostPID: false
allowHostPorts: false
allowPrivilegedContainer: false
allowedCapabilities: null
apiVersion: v1
defaultAddCapabilities: null
fsGroup:
type: MustRunAs
groups:
- system:authenticated
kind: SecurityContextConstraints
metadata:
annotations:
kubernetes.io/description: restricted denies
access to all host features and requires
pods to be run with a UID, and SELinux context
that are allocated to the namespace. This
is the most restrictive SCC.
creationTimestamp: 2016-12-22T10:04:27Z
name: restricted
resourceVersion: "102"
selfLink:
/api/v1/securitycontextconstraints/restricted
uid: 05f68498-c82e-11e6-b2bd-68f7286606f4
priority: null
readOnlyRootFilesystem: false
requiredDropCapabilities:
- KILL
- MKNOD
- SYS_CHROOT
- SETUID
- SETGID
runAsUser:
type: MustRunAsRange
seLinuxContext:
type: MustRunAs
supplementalGroups:
type: RunAsAny
volumes:
- configMap
- downwardAPI
- emptyDir
- persistentVolumeClaim
- secret
Dropped Capabilities
18. Editing *AGAIN* the ‘restricted’ SCC
$ oc edit scc restricted
securitycontextconstraints "restricted" edited
$ oc get scc restricted -o yaml | grep DropCap -A5
requiredDropCapabilities:
- KILL
- MKNOD
- SYS_CHROOT
runAsUser:
type: RunAsAny
$ oc logs wordpress-1-0kz6o
WordPress not found in /var/www/html - copying now...
Complete! WordPress has been successfully copied to /var/www/html
AH00558: apache2: Could not reliably determine the server's fully qualified domain name, using 172.17.0.4. Set
the 'ServerName' directive globally to suppress this message
AH00558: apache2: Could not reliably determine the server's fully qualified domain name, using 172.17.0.4. Set
the 'ServerName' directive globally to suppress this message
[Thu Jan 12 14:13:12.437336 2017] [mpm_prefork:notice] [pid 1] AH00163: Apache/2.4.10 (Debian) PHP/5.6.29
configured -- resuming normal operations
[Thu Jan 12 14:13:12.437365 2017] [core:notice] [pid 1] AH00094: Command line: 'apache2 -D FOREGROUND'
22. Inspecting the ‘anyuid’ SCC
$ oc get scc/anyuid -o yaml
allowHostDirVolumePlugin: false
allowHostIPC: false
allowHostNetwork: false
allowHostPID: false
allowHostPorts: false
allowPrivilegedContainer: false
allowedCapabilities: null
apiVersion: v1
defaultAddCapabilities: null
fsGroup:
type: RunAsAny
groups:
- system:cluster-admins
kind: SecurityContextConstraints
metadata:
annotations:
kubernetes.io/description: anyuid provides all
features of the restricted SCC
but allows users to run with any UID and any
GID. This is the default SCC for
authenticated users.
creationTimestamp: 2016-12-22T10:04:27Z
name: anyuid
resourceVersion: "103"
selfLink:
/api/v1/securitycontextconstraints/anyuid
uid: 05f6bbdb-c82e-11e6-b2bd-68f7286606f4
priority: 10
readOnlyRootFilesystem: false
requiredDropCapabilities:
- MKNOD
- SYS_CHROOT
runAsUser:
type: RunAsAny
seLinuxContext:
type: MustRunAs
supplementalGroups:
type: RunAsAny
volumes:
- configMap
- downwardAPI
- emptyDir
- persistentVolumeClaim
- secret
23. Use the ‘anyuid’
How can I do it?
Using Service Accounts
Service accounts provide a flexible way to
control API access without sharing a regular
user’s credentials.
Every service account has an associated
username that can be granted roles, just like a
regular user. The username is derived from its
project and name:
system:serviceaccount:<project>:<name>
24. Service Account creation and configuration
$ oc project wpoption2
Already on project "wpoption2" on server "https://192.168.123.1:8443".
$ oc create serviceaccount wp-sa
serviceaccount "wp-sa" created
$ oc whoami
system:admin
$ oc adm policy add-scc-to-user anyuid system:serviceaccount:wpoption2:wp-sa
$ oc get scc/anyuid -o yaml | tail
supplementalGroups:
type: RunAsAny
users:
- system:serviceaccount:wpoption2:wp-sa
volumes:
- configMap
- downwardAPI
- emptyDir
- persistentVolumeClaim
- secret
27. Check if it’s working...
$ oc logs wordpress-2-sqd8f
WordPress not found in /var/www/html - copying now...
Complete! WordPress has been successfully copied to /var/www/html
AH00558: apache2: Could not reliably determine the server's fully qualified domain name, using 172.17.0.9. Set
the 'ServerName' directive globally to suppress this message
AH00558: apache2: Could not reliably determine the server's fully qualified domain name, using 172.17.0.9. Set
the 'ServerName' directive globally to suppress this message
[Fri Jan 13 16:15:15.728548 2017] [mpm_prefork:notice] [pid 1] AH00163: Apache/2.4.10 (Debian) PHP/5.6.29
configured -- resuming normal operations
[Fri Jan 13 16:15:15.728574 2017] [core:notice] [pid 1] AH00094: Command line: 'apache2 -D FOREGROUND'
$ oc describe pod wordpress-2-sqd8f | head
Name: wordpress-2-sqd8f
Namespace: myproject
Security Policy: anyuid
Node: 192.168.123.1/192.168.123.1
Start Time: Fri, 13 Jan 2017 17:14:58 +0100
Labels: app=wordpress
deployment=wordpress-2
deploymentconfig=wordpress
Status: Running
IP: 172.17.0.9
29. Really?
Why rebuild the container image?
● You may not be OpenShift
cluster administrator
● All the previous solutions require
admin privileges
● You should not grant root on
thirdparty containers
30. First:
Locate the Dockerfile
● Search on
DockerHub/DockerStore:
https://store.docker.com/search?q
=wordpress&source=verified&ty
pe=image
● Download the Dockerfile:
https://github.com/docker-library
/wordpress/blob/7d40c4237f0189
2bb6dbc67d1a82f5b15f807ca1/ph
p5.6/apache/Dockerfile
● Make the edits and upload it
somewhere!
31. Inspecting default ‘wordpress’ Dockerfile
FROM php:5.6-apache
# install the PHP extensions we need
RUN set -ex;
apt-get update;
apt-get install -y
libjpeg-dev
libpng12-dev
;
rm -rf /var/lib/apt/lists/*;
docker-php-ext-configure gd --with-png-dir=/usr
--with-jpeg-dir=/usr;
docker-php-ext-install gd mysqli opcache
# TODO consider removing the *-dev deps and only keeping
the necessary lib* packages
# set recommended PHP.ini settings
# see
https://secure.php.net/manual/en/opcache.installation.php
RUN {
echo 'opcache.memory_consumption=128';
echo 'opcache.interned_strings_buffer=8';
echo 'opcache.max_accelerated_files=4000';
echo 'opcache.revalidate_freq=2';
echo 'opcache.fast_shutdown=1';
echo 'opcache.enable_cli=1';
} > /usr/local/etc/php/conf.d/opcache-recommended.ini
RUN a2enmod rewrite expires
VOLUME /var/www/html
ENV WORDPRESS_VERSION 4.7.1
ENV WORDPRESS_SHA1
8e56ba56c10a3f245c616b13e46bd996f63793d6
RUN set -ex;
curl -o wordpress.tar.gz -fSL
"https://wordpress.org/wordpress-${WORDPRESS_VERSION}.tar
.gz";
echo "$WORDPRESS_SHA1 *wordpress.tar.gz" | sha1sum -c
-;
# upstream tarballs include ./wordpress/ so this gives us
/usr/src/wordpress
tar -xzf wordpress.tar.gz -C /usr/src/;
rm wordpress.tar.gz;
chown -R www-data:www-data /usr/src/wordpress
COPY docker-entrypoint.sh /usr/local/bin/
ENTRYPOINT ["docker-entrypoint.sh"]
CMD ["apache2-foreground"]
32. Editing ‘wordpress’ Dockerfile
FROM php:5.6-apache
# install the PHP extensions we need
RUN set -ex;
apt-get update;
apt-get install -y
libjpeg-dev
libpng12-dev
;
rm -rf /var/lib/apt/lists/*;
docker-php-ext-configure gd --with-png-dir=/usr
--with-jpeg-dir=/usr;
docker-php-ext-install gd mysqli opcache
# TODO consider removing the *-dev deps and only keeping
the necessary lib* packages
# set recommended PHP.ini settings
# see
https://secure.php.net/manual/en/opcache.installation.php
RUN {
echo 'opcache.memory_consumption=128';
echo 'opcache.interned_strings_buffer=8';
echo 'opcache.max_accelerated_files=4000';
echo 'opcache.revalidate_freq=2';
echo 'opcache.fast_shutdown=1';
echo 'opcache.enable_cli=1';
} > /usr/local/etc/php/conf.d/opcache-recommended.ini
RUN a2enmod rewrite expires
VOLUME /var/www/html
RUN sed -i 's/Listen 80/Listen 8080/g'
/etc/apache2/ports.conf
EXPOSE 8080
RUN chmod g+w /var/log/apache2
RUN chmod g+w /var/lock/apache2
RUN chmod g+w /var/run/apache2
ENV WORDPRESS_VERSION 4.7
ENV WORDPRESS_SHA1
1e14144c4db71421dc4ed22f94c3914dfc3b7020
RUN set -ex;
curl -o wordpress.tar.gz -fSL
"https://wordpress.org/wordpress-${WORDPRESS_VERSION}.tar
.gz";
echo "$WORDPRESS_SHA1 *wordpress.tar.gz" | sha1sum -c
-;
# upstream tarballs include ./wordpress/ so this gives us
/usr/src/wordpress
tar -xzf wordpress.tar.gz -C /usr/src/;
rm wordpress.tar.gz;
chmod -R 777 /usr/src/wordpress
COPY docker-entrypoint.sh /usr/local/bin/
ENTRYPOINT ["docker-entrypoint.sh"]
CMD ["apache2-foreground"]
33. Editing container’s entrypoint: docker-entrypoint.sh
#!/bin/bash
#set -euo pipefail
# usage: file_env VAR [DEFAULT]
# ie: file_env 'XYZ_DB_PASSWORD' 'example'
# (will allow for "$XYZ_DB_PASSWORD_FILE" to fill in the value of
# "$XYZ_DB_PASSWORD" from a file, especially for Docker's secrets feature)
file_env() {
local var="$1"
local fileVar="${var}_FILE"
local def="${2:-}"
if [ "${!var:-}" ] && [ "${!fileVar:-}" ]; then
echo >&2 "error: both $var and $fileVar are set (but are exclusive)"
exit 1
fi
local val="$def"
if [ "${!var:-}" ]; then
val="${!var}"
elif [ "${!fileVar:-}" ]; then
val="$(< "${!fileVar}")"
fi
export "$var"="$val"
unset "$fileVar"
}
Bash strict run
34. Creating a new-app with Dockerfile source
$ oc new-app https://github.com/alezzandro/wordpress-in-userspace -e WORDPRESS_DB_HOST=mariadb -e
WORDPRESS_DB_USER=root -e WORDPRESS_DB_PASSWORD=mysecret
--> Found Docker image 01b23de (18 hours old) from Docker Hub for "php:5.6-apache"
* An image stream will be created as "php:5.6-apache" that will track the source image
* A Docker build using source code from https://github.com/alezzandro/wordpress-in-userspace will be
created
* The resulting image will be pushed to image stream "wordpress-in-userspace:latest"
* Every time "php:5.6-apache" changes a new build will be triggered
* This image will be deployed in deployment config "wordpress-in-userspace"
* Port 8080/tcp will be load balanced by service "wordpress-in-userspace"
* Other containers can access this service through the hostname "wordpress-in-userspace"
* WARNING: Image "php:5.6-apache" runs as the 'root' user which may not be permitted by your cluster
administrator
--> Creating resources with label app=wordpress-in-userspace ...
imagestream "php" created
imagestream "wordpress-in-userspace" created
buildconfig "wordpress-in-userspace" created
deploymentconfig "wordpress-in-userspace" created
service "wordpress-in-userspace" created
--> Success
Build scheduled, use 'oc logs -f bc/wordpress-in-userspace' to track its progress.
Run 'oc status' to view your app.
35. Adding ‘emptyDir’ volume and the supplementalGroup
$ oc volume dc/wordpress-in-userspace --add --name=wordpress-volume-1 -t emptyDir --mount-path=/var/www/html
deploymentconfigs/wordpress-in-userspace
$ oc edit dc/wordpress-in-userspace
deploymentconfigs/wordpress-in-userspace
$ oc get dc wordpress-in-userspace -o yaml|grep -A2 security
securityContext:
supplementalGroups:
- 33
$ oc get pods
NAME READY STATUS RESTARTS AGE
mariadb-1-sscl5 1/1 Running 0 40m
wordpress-in-userspace-1-build 0/1 Completed 0 34m
wordpress-in-userspace-3-isov6 1/1 Running 0 15m
$ oc rsh wordpress-in-userspace-3-isov6
$ id
uid=1000140000 gid=0(root) groups=0(root),33(www-data),1000140000
$ ls -ld /var/run/apache2
drwxrwxr-x. 2 www-data www-data 4096 Jan 23 17:27 /var/run/apache2
36. Create a route and check the result
$ oc create -f wp-route.yml
route/wordpress-in-userspace
$ curl http://wpoption3.192.168.123.1.xip.io 2>/dev/null|head
<!DOCTYPE html>
<html lang="en-US" class="no-js no-svg">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<link rel="profile" href="http://gmpg.org/xfn/11">
<script>(function(html){html.className =
html.className.replace(/bno-jsb/,'js')})(document.documentElement);</script>
<title>Test website – Just another WordPress site</title>
<meta name='robots' content='noindex,follow' />