ENVIRONMENT:
Kubernetes version: v1.16.3
OS: CentOS 7
Kernel: Linux k8s02-master01 3.10.0-1062.4.3.el7.x86_64
WHAT HAPPENED:
I have a Wordpress Deployment running a container built from a custom Apache/Wordpress image. I tried to upload plugins using the Wordpress admin, but the plugin folders default to 777 permission. Plugin folders ONLY, not their files. Noticed that /var/www/html
is set to 777 by default, then I tried to manually chmod 755 /var/www/html
in the container context... It works, new plugin folders default to 755, but it's not persistent. Tried to chmod in the Dockerfile, but it does not work, /var/www/html
still defaults to 777. Same issue when I use the official Wordpress image instead of my Dockerfile.
Is it possible to default /var/www/html
to 755 permission?
DOCKERFILE (wordpress-test:5.2.4-apache):
FROM wordpress:5.2.4-apache
RUN sed -i 's/Listen 80/Listen 8080/g' /etc/apache2/ports.conf;
RUN sed -i 's/:80/:8080/g' /etc/apache2/sites-enabled/000-default.conf;
RUN sed -i 's/#ServerName www.example.com/ServerName localhost/g' /etc/apache2/sites-enabled/000-default.conf;
RUN /bin/bash -c 'ls -la /var/www; chmod 755 /var/www/html; ls -la /var/www'
EXPOSE 8080
CMD ["apache2-foreground"]
DOCKERFILE BUILD LOGS:
Step 8/10 : RUN /bin/bash -c 'ls -la /var/www; chmod 755 /var/www/html; ls -la /var/www';
---> Running in 7051d46dd9f3
total 12
drwxr-xr-x 1 root root 4096 Oct 17 14:22 .
drwxr-xr-x 1 root root 4096 Oct 17 14:22 ..
drwxrwxrwx 2 www-data www-data 4096 Oct 17 14:28 html
total 12
drwxr-xr-x 1 root root 4096 Oct 17 14:22 .
drwxr-xr-x 1 root root 4096 Oct 17 14:22 ..
drwxr-xr-x 2 www-data www-data 4096 Oct 17 14:28 html
Checked result in the container context :
$ kubectl exec -it <POD_NAME> -n development -- sh
(inside the container) $ ls -la /var/www
total 12
drwxr-xr-x. 1 root root 4096 Oct 17 14:22 .
drwxr-xr-x 1 root root 4096 Oct 17 14:22 ..
drwxrwxrwx 5 www-data www-data 4096 Dec 17 05:40 html
/var/www/html
still defaults to 777.
DEPLOYMENT
apiVersion: apps/v1
kind: Deployment
metadata:
name: blog-wordpress
namespace: development
labels:
app: blog
spec:
selector:
matchLabels:
app: blog
tier: wordpress
replicas: 4
strategy:
type: RollingUpdate
rollingUpdate:
maxSurge: 2
maxUnavailable: 2
template:
metadata:
labels:
app: blog
tier: wordpress
spec:
volumes:
- name: blog-wordpress
persistentVolumeClaim:
claimName: blog-wordpress
containers:
- name: blog-wordpress
# image: wordpress:5.2.4-apache
image: wordpress-test:5.2.4-apache
securityContext:
runAsUser: 33
runAsGroup: 33
allowPrivilegeEscalation: false
capabilities:
add:
- "NET_ADMIN"
- "NET_BIND_SERVICE"
- "SYS_TIME"
resources:
requests:
cpu: "250m"
memory: "64Mi"
limits:
cpu: "500m"
memory: "128Mi"
ports:
- name: liveness-port
containerPort: 8080
readinessProbe:
initialDelaySeconds: 15
httpGet:
path: /index.php
port: 8080
timeoutSeconds: 15
periodSeconds: 15
failureThreshold: 5
livenessProbe:
initialDelaySeconds: 10
httpGet:
path: /index.php
port: 8080
timeoutSeconds: 10
periodSeconds: 15
failureThreshold: 5
env:
# Database
- name: WORDPRESS_DB_HOST
value: blog-mysql
- name: WORDPRESS_DB_NAME
value: wordpress
- name: WORDPRESS_DB_USER
valueFrom:
secretKeyRef:
name: blog-mysql
key: username
- name: WORDPRESS_DB_PASSWORD
valueFrom:
secretKeyRef:
name: blog-mysql
key: password
- name: WORDPRESS_TABLE_PREFIX
value: wp_
- name: WORDPRESS_AUTH_KEY
valueFrom:
secretKeyRef:
name: blog-wordpress
key: auth-key
- name: WORDPRESS_SECURE_AUTH_KEY
valueFrom:
secretKeyRef:
name: blog-wordpress
key: secure-auth-key
- name: WORDPRESS_LOGGED_IN_KEY
valueFrom:
secretKeyRef:
name: blog-wordpress
key: logged-in-key
- name: WORDPRESS_NONCE_KEY
valueFrom:
secretKeyRef:
name: blog-wordpress
key: nonce-key
- name: WORDPRESS_AUTH_SALT
valueFrom:
secretKeyRef:
name: blog-wordpress
key: auth-salt
- name: WORDPRESS_SECURE_AUTH_SALT
valueFrom:
secretKeyRef:
name: blog-wordpress
key: secure-auth-salt
- name: WORDPRESS_LOGGED_IN_SALT
valueFrom:
secretKeyRef:
name: blog-wordpress
key: logged-in-salt
- name: WORDPRESS_NONCE_SALT
valueFrom:
secretKeyRef:
name: blog-wordpress
key: nonce-salt
- name: WORDPRESS_CONFIG_EXTRA
value: |
define('WPLANG', 'fr_FR');
define('WP_CACHE', false);
define('WP_MEMORY_LIMIT', '64M');
volumeMounts:
- name: blog-wordpress
mountPath: "/var/www/html/wp-content"
/etc/apache2/apache2.conf
# This is the main Apache server configuration file. It contains the
# configuration directives that give the server its instructions.
# See http://httpd.apache.org/docs/2.4/ for detailed information about
# the directives and /usr/share/doc/apache2/README.Debian about Debian specific
# hints.
#
#
# Summary of how the Apache 2 configuration works in Debian:
# The Apache 2 web server configuration in Debian is quite different to
# upstream's suggested way to configure the web server. This is because Debian's
# default Apache2 installation attempts to make adding and removing modules,
# virtual hosts, and extra configuration directives as flexible as possible, in
# order to make automating the changes and administering the server as easy as
# possible.
# It is split into several files forming the configuration hierarchy outlined
# below, all located in the /etc/apache2/ directory:
#
# /etc/apache2/
# |-- apache2.conf
# | `-- ports.conf
# |-- mods-enabled
# | |-- *.load
# | `-- *.conf
# |-- conf-enabled
# | `-- *.conf
# `-- sites-enabled
# `-- *.conf
#
#
# * apache2.conf is the main configuration file (this file). It puts the pieces
# together by including all remaining configuration files when starting up the
# web server.
#
# * ports.conf is always included from the main configuration file. It is
# supposed to determine listening ports for incoming connections which can be
# customized anytime.
#
# * Configuration files in the mods-enabled/, conf-enabled/ and sites-enabled/
# directories contain particular configuration snippets which manage modules,
# global configuration fragments, or virtual host configurations,
# respectively.
#
# They are activated by symlinking available configuration files from their
# respective *-available/ counterparts. These should be managed by using our
# helpers a2enmod/a2dismod, a2ensite/a2dissite and a2enconf/a2disconf. See
# their respective man pages for detailed information.
#
# * The binary is called apache2. Due to the use of environment variables, in
# the default configuration, apache2 needs to be started/stopped with
# /etc/init.d/apache2 or apache2ctl. Calling /usr/bin/apache2 directly will not
# work with the default configuration.
# Global configuration
#
#
# ServerRoot: The top of the directory tree under which the server's
# configuration, error, and log files are kept.
#
# NOTE! If you intend to place this on an NFS (or otherwise network)
# mounted filesystem then please read the Mutex documentation (available
# at <URL:http://httpd.apache.org/docs/2.4/mod/core.html#mutex>);
# you will save yourself a lot of trouble.
#
# Do NOT add a slash at the end of the directory path.
#
#ServerRoot "/etc/apache2"
#
# The accept serialization lock file MUST BE STORED ON A LOCAL DISK.
#
#Mutex file:${APACHE_LOCK_DIR} default
#
# The directory where shm and other runtime files will be stored.
#
DefaultRuntimeDir ${APACHE_RUN_DIR}
#
# PidFile: The file in which the server should record its process
# identification number when it starts.
# This needs to be set in /etc/apache2/envvars
#
PidFile ${APACHE_PID_FILE}
#
# Timeout: The number of seconds before receives and sends time out.
#
Timeout 300
#
# KeepAlive: Whether or not to allow persistent connections (more than
# one request per connection). Set to "Off" to deactivate.
#
KeepAlive On
#
# MaxKeepAliveRequests: The maximum number of requests to allow
# during a persistent connection. Set to 0 to allow an unlimited amount.
# We recommend you leave this number high, for maximum performance.
#
MaxKeepAliveRequests 100
#
# KeepAliveTimeout: Number of seconds to wait for the next request from the
# same client on the same connection.
#
KeepAliveTimeout 5
# These need to be set in /etc/apache2/envvars
User ${APACHE_RUN_USER}
Group ${APACHE_RUN_GROUP}
#
# HostnameLookups: Log the names of clients or just their IP addresses
# e.g., www.apache.org (on) or 204.62.129.132 (off).
# The default is off because it'd be overall better for the net if people
# had to knowingly turn this feature on, since enabling it means that
# each client request will result in AT LEAST one lookup request to the
# nameserver.
#
HostnameLookups Off
# ErrorLog: The location of the error log file.
# If you do not specify an ErrorLog directive within a <VirtualHost>
# container, error messages relating to that virtual host will be
# logged here. If you *do* define an error logfile for a <VirtualHost>
# container, that host's errors will be logged there and not here.
#
ErrorLog ${APACHE_LOG_DIR}/error.log
#
# LogLevel: Control the severity of messages logged to the error_log.
# Available values: trace8, ..., trace1, debug, info, notice, warn,
# error, crit, alert, emerg.
# It is also possible to configure the log level for particular modules, e.g.
# "LogLevel info ssl:warn"
#
LogLevel warn
# Include module configuration:
IncludeOptional mods-enabled/*.load
IncludeOptional mods-enabled/*.conf
# Include list of ports to listen on
Include ports.conf
# Sets the default security model of the Apache2 HTTPD server. It does
# not allow access to the root filesystem outside of /usr/share and /var/www.
# The former is used by web applications packaged in Debian,
# the latter may be used for local directories served by the web server. If
# your system is serving content from a sub-directory in /srv you must allow
# access here, or in any related virtual host.
<Directory />
Options FollowSymLinks
AllowOverride None
Require all denied
</Directory>
<Directory /usr/share>
AllowOverride None
Require all granted
</Directory>
<Directory /var/www/>
Options Indexes FollowSymLinks
AllowOverride None
Require all granted
</Directory>
#<Directory /srv/>
# Options Indexes FollowSymLinks
# AllowOverride None
# Require all granted
#</Directory>
# AccessFileName: The name of the file to look for in each directory
# for additional configuration directives. See also the AllowOverride
# directive.
#
AccessFileName .htaccess
#
# The following lines prevent .htaccess and .htpasswd files from being
# viewed by Web clients.
#
<FilesMatch "^\.ht">
Require all denied
</FilesMatch>
#
# The following directives define some format nicknames for use with
# a CustomLog directive.
#
# These deviate from the Common Log Format definitions in that they use %O
# (the actual bytes sent including headers) instead of %b (the size of the
# requested file), because the latter makes it impossible to detect partial
# requests.
#
# Note that the use of %{X-Forwarded-For}i instead of %h is not recommended.
# Use mod_remoteip instead.
#
LogFormat "%v:%p %a %l %u %t \"%r\" %>s %O \"%{Referer}i\" \"%{User-Agent}i\"" vhost_combined
LogFormat "%a %l %u %t \"%r\" %>s %O \"%{Referer}i\" \"%{User-Agent}i\"" combined
LogFormat "%a %l %u %t \"%r\" %>s %O" common
LogFormat "%{Referer}i -> %U" referer
LogFormat "%{User-agent}i" agent
# Include of directories ignores editors' and dpkg's backup files,
# see README.Debian for details.
# Include generic snippets of statements
IncludeOptional conf-enabled/*.conf
# Include the virtual host configurations:
IncludeOptional sites-enabled/*.conf
/etc/apache2/ports.conf
# If you just change the port or add more ports here, you will likely also
# have to change the VirtualHost statement in
# /etc/apache2/sites-enabled/000-default.conf
Listen 8080
<IfModule ssl_module>
Listen 443
</IfModule>
<IfModule mod_gnutls.c>
Listen 443
</IfModule>
/etc/apache2/envvars
# envvars - default environment variables for apache2ctl
# this won't be correct after changing uid
unset HOME
# for supporting multiple apache2 instances
if [ "${APACHE_CONFDIR##/etc/apache2-}" != "${APACHE_CONFDIR}" ] ; then
SUFFIX="-${APACHE_CONFDIR##/etc/apache2-}"
else
SUFFIX=
fi
# Since there is no sane way to get the parsed apache2 config in scripts, some
# settings are defined via environment variables and then used in apache2ctl,
# /etc/init.d/apache2, /etc/logrotate.d/apache2, etc.
: ${APACHE_RUN_USER:=www-data}
export APACHE_RUN_USER
: ${APACHE_RUN_GROUP:=www-data}
export APACHE_RUN_GROUP
# temporary state file location. This might be changed to /run in Wheezy+1
: ${APACHE_PID_FILE:=/var/run/apache2$SUFFIX/apache2.pid}
export APACHE_PID_FILE
: ${APACHE_RUN_DIR:=/var/run/apache2$SUFFIX}
export APACHE_RUN_DIR
: ${APACHE_LOCK_DIR:=/var/lock/apache2$SUFFIX}
export APACHE_LOCK_DIR
# Only /var/log/apache2 is handled by /etc/logrotate.d/apache2.
: ${APACHE_LOG_DIR:=/var/log/apache2$SUFFIX}
export APACHE_LOG_DIR
## The locale used by some modules like mod_dav
: ${LANG:=C}
export LANG
## Uncomment the following line to use the system default locale instead:
#. /etc/default/locale
export LANG
## The command to get the status for 'apache2ctl status'.
## Some packages providing 'www-browser' need '--dump' instead of '-dump'.
#export APACHE_LYNX='www-browser -dump'
## If you need a higher file descriptor limit, uncomment and adjust the
## following line (default is 8192):
#APACHE_ULIMIT_MAX_FILES='ulimit -n 65536'
## If you would like to pass arguments to the web server, add them below
## to the APACHE_ARGUMENTS environment.
#export APACHE_ARGUMENTS=''
## Enable the debug mode for maintainer scripts.
## This will produce a verbose output on package installations of web server modules and web application
## installations which interact with Apache
#export APACHE2_MAINTSCRIPT_DEBUG=1
/etc/apache2/conf-enabled/docker-php.conf
<FilesMatch \.phpgt;
SetHandler application/x-httpd-php
</FilesMatch>
DirectoryIndex disabled
DirectoryIndex index.php index.html
<Directory /var/www/>
Options -Indexes
AllowOverride All
</Directory>
/etc/apache2/sites-enabled/000-default.conf
<VirtualHost *:8080>
# The ServerName directive sets the request scheme, hostname and port that
# the server uses to identify itself. This is used when creating
# redirection URLs. In the context of virtual hosts, the ServerName
# specifies what hostname must appear in the request's Host: header to
# match this virtual host. For the default virtual host (this file) this
# value is not decisive as it is used as a last resort host regardless.
# However, you must set it for any further virtual host explicitly.
ServerName localhost
ServerAdmin webmaster@localhost
DocumentRoot /var/www/html
# Available loglevels: trace8, ..., trace1, debug, info, notice, warn,
# error, crit, alert, emerg.
# It is also possible to configure the loglevel for particular
# modules, e.g.
#LogLevel info ssl:warn
ErrorLog ${APACHE_LOG_DIR}/error.log
CustomLog ${APACHE_LOG_DIR}/access.log combined
# For most configuration files from conf-available/, which are
# enabled or disabled at a global level, it is possible to
# include a line for only one particular virtual host. For example the
# following line enables the CGI configuration for this host only
# after it has been globally disabled with "a2disconf".
#Include conf-available/serve-cgi-bin.conf
</VirtualHost>
In kubernetes you can change permissions of the mounted volume with help of the initContainers. Your deployment may look like this:
apiVersion: apps/v1
kind: Deployment
metadata:
name: blog-wordpress
namespace: development
labels:
app: blog
spec:
selector:
matchLabels:
app: blog
tier: wordpress
replicas: 4
strategy:
type: RollingUpdate
rollingUpdate:
maxSurge: 2
maxUnavailable: 2
template:
metadata:
labels:
app: blog
tier: wordpress
spec:
volumes:
- name: blog-wordpress
persistentVolumeClaim:
claimName: blog-wordpress
initContainers:
- name: permission-fix
image: busybox
command: ["/bin/chmod","-R","755", "/var/www/html"]
volumeMounts:
- name: blog-wordpress
mountPath: /var/www/html/wp-content
containers:
- name: blog-wordpress
# image: wordpress:5.2.4-apache
image: wordpress-test:5.2.4-apache
securityContext:
runAsUser: 33
runAsGroup: 33
allowPrivilegeEscalation: false
capabilities:
add:
- "NET_ADMIN"
- "NET_BIND_SERVICE"
- "SYS_TIME"
resources:
requests:
cpu: "250m"
memory: "64Mi"
limits:
cpu: "500m"
memory: "128Mi"
ports:
- name: liveness-port
containerPort: 8080
readinessProbe:
initialDelaySeconds: 15
httpGet:
path: /index.php
port: 8080
timeoutSeconds: 15
periodSeconds: 15
failureThreshold: 5
livenessProbe:
initialDelaySeconds: 10
httpGet:
path: /index.php
port: 8080
timeoutSeconds: 10
periodSeconds: 15
failureThreshold: 5
env:
# Database
- name: WORDPRESS_DB_HOST
value: blog-mysql
- name: WORDPRESS_DB_NAME
value: wordpress
- name: WORDPRESS_DB_USER
valueFrom:
secretKeyRef:
name: blog-mysql
key: username
- name: WORDPRESS_DB_PASSWORD
valueFrom:
secretKeyRef:
name: blog-mysql
key: password
- name: WORDPRESS_TABLE_PREFIX
value: wp_
- name: WORDPRESS_AUTH_KEY
valueFrom:
secretKeyRef:
name: blog-wordpress
key: auth-key
- name: WORDPRESS_SECURE_AUTH_KEY
valueFrom:
secretKeyRef:
name: blog-wordpress
key: secure-auth-key
- name: WORDPRESS_LOGGED_IN_KEY
valueFrom:
secretKeyRef:
name: blog-wordpress
key: logged-in-key
- name: WORDPRESS_NONCE_KEY
valueFrom:
secretKeyRef:
name: blog-wordpress
key: nonce-key
- name: WORDPRESS_AUTH_SALT
valueFrom:
secretKeyRef:
name: blog-wordpress
key: auth-salt
- name: WORDPRESS_SECURE_AUTH_SALT
valueFrom:
secretKeyRef:
name: blog-wordpress
key: secure-auth-salt
- name: WORDPRESS_LOGGED_IN_SALT
valueFrom:
secretKeyRef:
name: blog-wordpress
key: logged-in-salt
- name: WORDPRESS_NONCE_SALT
valueFrom:
secretKeyRef:
name: blog-wordpress
key: nonce-salt
- name: WORDPRESS_CONFIG_EXTRA
value: |
define('WPLANG', 'fr_FR');
define('WP_CACHE', false);
define('WP_MEMORY_LIMIT', '64M');
volumeMounts:
- name: blog-wordpress
mountPath: "/var/www/html/wp-content"
EDIT: However keep in mind that you can only change permissions for the mounted folder, not it's parent folder/folders. So in the example above you can use:
command: ["/bin/chmod","-R","755", "/var/www/html"]
but it will change permissions only of /var/www/html/wp-content
directory. If you can prepare your volume so it contains /var/www/html
directory and can be mounted as such, you'll be able to set its permissions.
Let me know if it helped.