3

I have setup a containerized Wordpress project as a Azure App Service based on the official Wordpress Docker image where I have made no modifications to the image itself other than adding a SSH server based on the instructions given by Azure. This is what the Dockerfile looks like:

FROM wordpress:6.0.3-php7.4

########################################## Add SSH support for Azure ##########################################
# Install OpenSSH and set the password for root to "Docker!".
RUN apt update \
    && apt install -y openssh-server \
    && rm -rf /var/lib/apt/lists/*

RUN echo "root:Docker!" | chpasswd

# Copy the sshd_config file to the /etc/ssh/ directory
COPY docker/ssh/sshd_config /etc/ssh/

# Copy and configure the ssh_setup file
RUN mkdir -p /tmp
COPY docker/ssh/ssh_setup.sh /tmp
RUN chmod +x /tmp/ssh_setup.sh \
    && (sleep 1;/tmp/ssh_setup.sh 2>&1 > /dev/null)

# Open port 2222 for SSH access
EXPOSE 80 2222
###############################################################################################################

COPY docker/script.sh script.sh
RUN chmod +x script.sh
CMD []
ENTRYPOINT ["./script.sh"]

And docker/script.sh

#!/bin/bash
exec service ssh start &
exec /usr/local/bin/docker-entrypoint.sh apache2-foreground

On the App Service I have added the WEBSITES_ENABLE_APP_SERVICE_STORAGE=true application setting to enable persistent storage as well as set the WORDPRESS_DB_HOST, WORDPRESS_DB_NAME, WORDPRESS_DB_PASSWORD and WORDPRESS_DB_USER settings to connect to my database running on a another host.

When accessing the app service page in the browser and going through the Wordpress setup I can easily upload new files which are placed in the file system at /var/www/html/wp-content/uploads/<year>/<month>/<filename> which I can then access in my browser at https://my-app-service.azurewebsites.net/wp-content/uploads/<year>/<month>/<filename>.

With Azure only persisting data written in /home I instead tried to move the /var/www/html/wp-content/uploads directory to /home/uploads and then create a symbolic link to this from the expected path like so (the symbolic link creation could then also be added to the Dockerfile to automate this during deployment):

$ cd /var/www/html/wp-content
$ mv uploads /home/uploads
$ ln -s /home/uploads uploads

Now however, when I access https://my-app-service.azurewebsites.net/wp-content/uploads/<year>/<month>/<filename> I just get an empty 400 response.

In order to see if this was some sort of limitation of Azure I decided to try something similar with the most simple Python page instead. Dockerfile:

FROM python:3.10.0
RUN mkdir -p /var/www/html
WORKDIR /var/www/html

########################################## Add SSH support for Azure ##########################################
# Install OpenSSH and set the password for root to "Docker!".
RUN apt update \
    && apt install -y openssh-server \
    && rm -rf /var/lib/apt/lists/*

RUN echo "root:Docker!" | chpasswd

# Copy the sshd_config file to the /etc/ssh/ directory
COPY docker/ssh/sshd_config /etc/ssh/

# Copy and configure the ssh_setup file
RUN mkdir -p /tmp
COPY docker/ssh/ssh_setup.sh /tmp
RUN chmod +x /tmp/ssh_setup.sh \
    && (sleep 1;/tmp/ssh_setup.sh 2>&1 > /dev/null)

# Open port 2222 for SSH access
EXPOSE 80 2222
###############################################################################################################

COPY docker/script.sh script.sh
RUN chmod +x script.sh
CMD []
ENTRYPOINT ["./script.sh"]

And docker/script.sh

#!/bin/bash
exec service ssh start &
exec python -m http.server 80

Doing the same thing here works, so it doesn't seem to be a limitation with Azure. What I don't understand, however, is that the Wordpress docker image with the symbolic link works as expected running on my local machine.

What am I doing wrong? Why does the Python project work but not the Wordpress one?

Oskar Persson
  • 6,605
  • 15
  • 63
  • 124
  • Please, does the application or App Service report any errors? Probably the error could be related to some kind of permissions issue, even a bad function of the symlink (honestly I am unable to tell you if it is actually supported or not in this use case). My best advice is using [Azure Storage](https://learn.microsoft.com/en-us/azure/app-service/configure-connect-to-azure-storage?tabs=portal&pivots=container-linux) based on Azure Files instead to store your uploads information or event your entire `/var/www/html` content. – jccampanero Oct 25 '22 at 21:30
  • I have not been able to find any logs containing any errors or warnings. Do you have any idea on where I could find this? I have considered using Azure Storage however I would prefer not to since that’s another cost and I already get 250gb storage with my App Servive plan that I would like to use for this. – Oskar Persson Oct 25 '22 at 21:40
  • I understand Oskar, it is perfectly fine. Probably you could access the logs using SSH as you configured it. In addition, you could use [Azure CLI](https://learn.microsoft.com/en-us/azure/app-service/configure-custom-container?pivots=container-linux#access-diagnostic-logs-1) to access the log information. I am not sure about that, but probably you could use Kudu as well for accessing the container log information (please, try this first). See this [related SO question](https://stackoverflow.com/questions/52245077/where-can-i-find-docker-container-logs-for-azure-app-service). I hope it helps. – jccampanero Oct 25 '22 at 21:49
  • Have you tried chown/chmod the `/home/uploads` folder to be readable/writable by your web server and PHP Linux user? Can you check your Apache/Nginx configuration for the "follow symlink" option, and if `root`/rules allow this folder to be served? Do you have anything custom in htaccess that could prevent access? When you upload from WP, the file is created and correct, but not accessible from HTTP right? Try a simple txt file with chmod and checked nginx/apache/htaccess/permissions configs. – Mtxz Oct 26 '22 at 02:49
  • @Mtxz I have tried chown/chmod the /home/uploads directory but it doesn't seem to have any effect at all. I have also tried changing the Apache configuration by adding the FollowSymlink option but this also doesn't do anything. It is possible that there are multiple configuration files that I need to do this in but I'm not sure which ones in that case. Everything is kept as it is written in the Dockerfile, no other configuration is made. You can find the source code of the base image (wordpress:6.0.3-php7.4) here https://github.com/docker-library/wordpress/tree/master/latest/php7.4/apache – Oskar Persson Oct 26 '22 at 06:14
  • @jccampanero I will see if I can find anything using that – Oskar Persson Oct 26 '22 at 06:15
  • @jccampanero I'm only seeing 200 OK responses in the logs, e.g. ""GET /wp-content/uploads/2022/10/Screenshot-2022-10-26-090146.png HTTP/1.1" 200 52644 "-" "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/106.0.0.0 Safari/537.36"" while I'm in Google Chrome is getting the following: "This page isn’t working. If the problem continues, contact the site owner. HTTP ERROR 400" – Oskar Persson Oct 26 '22 at 07:13
  • I know you solved this, but have you checked if your 400 was related to incorrect headears? see https://stackoverflow.com/questions/56586353/azure-file-storage-the-server-responded-with-a-status-of-400-condition-headers and https://stackoverflow.com/questions/43706605/azure-file-storage-error-condition-headers-are-not-supported - to test just add a parameter at the end of media URL eg "?timestamp=1234" – Mtxz Oct 26 '22 at 10:17
  • Does not seem like it. I reverted my changes to get a 400 response again and added `?timestamp=1234` which also results in the same 400 response – Oskar Persson Oct 26 '22 at 10:21

2 Answers2

4

Solved it by adding an Alias instead of a symbolic link and disabling MMAP:

docker/extra.conf:

Alias /wp-content/uploads/ "/home/uploads/"
<Directory "/home/uploads/">
        Options Indexes MultiViews
        AllowOverride None
        Require all granted
</Directory>

<Directory "/home/uploads">
  EnableMMAP Off
</Directory>

docker/script.sh

#!/bin/bash
exec service ssh start &
exec /usr/local/bin/docker-entrypoint.sh apache2-foreground

Dockerfile

FROM wordpress:6.0.3-php7.4

########################################## Add SSH support for Azure ##########################################
# Install OpenSSH and set the password for root to "Docker!".
RUN apt update \
    && apt install -y openssh-server \
    && rm -rf /var/lib/apt/lists/*

RUN echo "root:Docker!" | chpasswd

# Copy the sshd_config file to the /etc/ssh/ directory
COPY docker/ssh/sshd_config /etc/ssh/

# Copy and configure the ssh_setup file
RUN mkdir -p /tmp
COPY docker/ssh/ssh_setup.sh /tmp
RUN chmod +x /tmp/ssh_setup.sh \
    && (sleep 1;/tmp/ssh_setup.sh 2>&1 > /dev/null)

# Open port 2222 for SSH access
EXPOSE 80 2222
###############################################################################################################

COPY docker/apache/extra.conf /etc/apache2/extra.conf
RUN cat /etc/apache2/extra.conf >> /etc/apache2/apache2.conf

COPY docker/script.sh /usr/local/bin/script.sh
RUN chmod +x /usr/local/bin/script.sh
CMD []
ENTRYPOINT ["/usr/local/bin/script.sh"]
Oskar Persson
  • 6,605
  • 15
  • 63
  • 124
2

You have to change the configuration of apache webserver so, that it can follow symbolic links:

<VirtualHost *:80>
    DocumentRoot /var/www
    <Directory />
        Options FollowSymLinks
        AllowOverride None
    </Directory>
Slava Kuravsky
  • 2,702
  • 10
  • 16
  • In which file should I do this? Note that I'm using the wordpress:6.0.3-php7.4 docker image. – Oskar Persson Oct 26 '22 at 07:09
  • The main config is in `/etc/apache2/apache2.conf`, but I think you need to modify `/etc/apache2/conf-available/docker-php.conf` – Slava Kuravsky Oct 26 '22 at 07:22
  • I updated `/etc/apache2/conf-available/docker-php.conf` to the following but it didn't have any effetc: SetHandler application/x-httpd-php DirectoryIndex disabled DirectoryIndex index.php index.html Options -Indexes +FollowSymLinks AllowOverride All – Oskar Persson Oct 26 '22 at 07:44
  • Maybe directories don't match. Try to create link in /var/www to test. Also you can try to change config to – Slava Kuravsky Oct 26 '22 at 08:14
  • That didn't work either – Oskar Persson Oct 26 '22 at 08:19
  • How do you start container and reload configuration? – Slava Kuravsky Oct 26 '22 at 08:29
  • Any changes I want to make to the configuration I add as new files to my project repository and copy in using the Dockerfile, overwriting any files in the base Docker image. I upload the updated Docker image to my Azure Container Registry and then manually restart the Azure App Service through the Azure Portal which will pull the latest image from the registry upon restart. I then verify that the configuration has been made using SSH. – Oskar Persson Oct 26 '22 at 08:32
  • Check the owner of your link and target directory, set it to www-data:www-data – Slava Kuravsky Oct 26 '22 at 08:41
  • Running `chown -R www-data:www-data /home/uploads` does not seem to have any effect as everything in `/home` on Azure containers always seem to be owned by `nobody:nogroup`. And FollowSymlinks don't seem to be the problem either seeing as setting a symlink from `/var/www/html/wp-content/uploads` to `/var/www/html/wp-content/uploads2` works without any problems. The problem seems to be in how Apache handles Azures use of /home. See https://learn.microsoft.com/en-us/azure/app-service/configure-custom-container?pivots=container-linux#use-persistent-shared-storage for some information on it – Oskar Persson Oct 26 '22 at 08:45
  • So you have in your docker run command `-v /home/upload:/home/upload`? – Slava Kuravsky Oct 26 '22 at 08:55
  • No, I have not configured how docker is started at all. All this is handled by the Azure App Service. – Oskar Persson Oct 26 '22 at 08:57
  • Also, if I instead use wordpress:6.0.3-php7.4-fpm as the base image (notice the -fpm at the end) and use Caddy instead of Apache, it works. So the problem is with Apache. And I want to use Apache, not Caddy and not Nginx. – Oskar Persson Oct 26 '22 at 09:08
  • Solved it. See my own answer – Oskar Persson Oct 26 '22 at 10:11