<p><strong>ENVIRONMENT:</strong></p>

<pre><code>Kubernetes version: v1.16.3  
OS: CentOS 7  
Kernel: Linux k8s02-master01 3.10.0-1062.4.3.el7.x86_64
</code></pre>

<p><strong>WHAT HAPPENED:</strong></p>

<p>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 <code>/var/www/html</code> is set to 777 by default, then I tried to manually <code>chmod 755 /var/www/html</code> 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, <code>/var/www/html</code> still defaults to 777. Same issue when I use the official Wordpress image instead of my Dockerfile.</p>

<p>Is it possible to default <code>/var/www/html</code> to 755 permission?</p>

<p><strong>DOCKERFILE (wordpress-test:5.2.4-apache):</strong></p>

<pre><code>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"]
</code></pre>

<p><strong>DOCKERFILE BUILD LOGS:</strong></p>

<pre><code>Step 8/10 : RUN /bin/bash -c 'ls -la /var/www; chmod 755 /var/www/html; ls -la /var/www';
 ---&gt; 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
</code></pre>

<p>Checked result in the container context :</p>

<pre><code>$ kubectl exec -it &lt;POD_NAME&gt; -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
</code></pre>

<p><code>/var/www/html</code> still defaults to 777.</p>

<p><strong>DEPLOYMENT</strong></p>

<pre><code>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"
</code></pre>

<p><strong>/etc/apache2/apache2.conf</strong></p>

<pre><code># 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 &lt;URL:http://httpd.apache.org/docs/2.4/mod/core.html#mutex&gt;);
# 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 &lt;VirtualHost&gt;
# container, error messages relating to that virtual host will be
# logged here.  If you *do* define an error logfile for a &lt;VirtualHost&gt;
# 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.
&lt;Directory /&gt;
    Options FollowSymLinks
    AllowOverride None
    Require all denied
&lt;/Directory&gt;

&lt;Directory /usr/share&gt;
    AllowOverride None
    Require all granted
&lt;/Directory&gt;

&lt;Directory /var/www/&gt;
    Options Indexes FollowSymLinks
    AllowOverride None
    Require all granted
&lt;/Directory&gt;

#&lt;Directory /srv/&gt;
#   Options Indexes FollowSymLinks
#   AllowOverride None
#   Require all granted
#&lt;/Directory&gt;




# 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.
#
&lt;FilesMatch "^\.ht"&gt;
    Require all denied
&lt;/FilesMatch&gt;


#
# 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\" %&gt;s %O \"%{Referer}i\" \"%{User-Agent}i\"" vhost_combined
LogFormat "%a %l %u %t \"%r\" %&gt;s %O \"%{Referer}i\" \"%{User-Agent}i\"" combined
LogFormat "%a %l %u %t \"%r\" %&gt;s %O" common
LogFormat "%{Referer}i -&gt; %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
</code></pre>

<p><strong>/etc/apache2/ports.conf</strong></p>

<pre><code># 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

&lt;IfModule ssl_module&gt;
    Listen 443
&lt;/IfModule&gt;

&lt;IfModule mod_gnutls.c&gt;
    Listen 443
&lt;/IfModule&gt;
</code></pre>

<p><strong>/etc/apache2/envvars</strong></p>

<pre><code># 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
</code></pre>

<p><strong>/etc/apache2/conf-enabled/docker-php.conf</strong></p>

<pre><code>&lt;FilesMatch \.php$&gt;
    SetHandler application/x-httpd-php
&lt;/FilesMatch&gt;

DirectoryIndex disabled
DirectoryIndex index.php index.html

&lt;Directory /var/www/&gt;
    Options -Indexes
    AllowOverride All
&lt;/Directory&gt;
</code></pre>

<p><strong>/etc/apache2/sites-enabled/000-default.conf</strong></p>

<pre><code>&lt;VirtualHost *:8080&gt;
    # 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
&lt;/VirtualHost&gt;
</code></pre>

iamcryptoki
<p>In <strong>kubernetes</strong> you can change permissions of the mounted volume with help of the <a href="https://kubernetes.io/docs/concepts/workloads/pods/init-containers/" rel="nofollow noreferrer">initContainers</a>. Your deployment may look like this:</p>

<pre><code>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"
</code></pre>

<p><strong>EDIT:</strong>
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:</p>

<pre><code>command: ["/bin/chmod","-R","755", "/var/www/html"]
</code></pre>

<p>but it will change permissions only of <code>/var/www/html/wp-content</code> directory.
If you can prepare your volume so it contains <code>/var/www/html</code> directory and can be mounted as such, you'll be able to set its permissions.</p>

<p>Let me know if it helped.</p>
