SSHD2 Update Wordpress. "Unable to locate WordPress Content directory (wp-content)."

January 13, 2017 3.3k views
Apache CMS Configuration Management

I am unable to pinpoint this error. I am trying to use an SSH key and unique user for this particular site. FS_METHOD direct is of course making files under the www-data user, which I don't want (I don't think). So I'm kinda stuck here. No matter which settings I change in the config file that's the error I get (after fixing the public/private key mismatch error hah). And in the tutorial I didn't see much talk about this error. Any help pointing me in the right direction would be much appreciated!

4 Answers
jtittle1 January 13, 2017
Accepted Answer

@danfoote104227

For Apache, then, you may need to run ownership equal to user:apacheuser where apacheuser is the user that Apache is running as. On NGINX, you have a little more control as you can actually run PHP as a different user than you run NGINX as (i.e. the user you created), so PHP-FPM will run as intended without the need for work arounds.

With Apache, and without the ability to run PHP as a different user per instance (as you can with PHP-FPM), then you may be forced to use define() and the plugins. That is, unless you run all files and dirs as the same user as apache is running as, which isn't very secure as one user then has control over all files and directories associated with your accounts.

In such a case, should someone gain access to one, they could easily gain access to the rest.

  • @jtittle

    I suppose I should in this case then. I was looking for some security . Which I suppose I will be getting with using SSH keys and isolating users with the plugin (just wish I could ditch the plugin! haha) Thank you very much for the insight.

    • @danfoote104227

      No problem. If you have the time to test NGINX, and want to see how it works, I just replied to another users question with full details on how to get setup rather quickly from the CLI.

      https://www.digitalocean.com/community/questions/ip-visitor-comments-is-exactly-like-my-ip-droplet-so-i-can-not-cut-off-the-spammers-by-their-ip?answer=32882

      That comment above includes all the commands you need to automate a source compile of NGINX as well as the packages needed for PHP 7.1 + PHP-FPM, and MariaDB (i.e. you walk away with an easily setup NGINX, PHP-FPM, and MariaDB configuration with minimal effort).

      I ran the entire setup on a Droplet about 15 minutes ago and the only thing you need to do other than what I've shown in that mini-guide is to change permissions on the directory you set as root in the server block to match the user that PHP-FPM is running as.

      The default PHP-FPM www.conf file runs as www-data, so essentially you'd run:

      sudo chown -R www-data:www-data /home/username
      

      and once that's done, no need for plugins as PHP is now running as the user that owns the directories and files.

      That's one big plus for NGINX and one of many reasons I prefer it over Apache. Not trying to get you to change, just giving you a few options.

      • This is really neat I will spin up a fresh server and give it a shot! I originally was going for a LEMP server, but the PHP-FPM gave me trouble as I wanted to secure each individual user from each other. There just weren't enough tutorials focused on nginx so I abandoned it. YOUR TUTORIAL THOUGH... I'm already up and running. I really hope you continue it and explain the security and separating the users. This is pure gold. Thanks again!

        • @danfoote104227

          Glad to hear that it helped!

          If you have any specific questions, feel free to ask. The majority of web server work I do revolves around NGINX, so compiling from source is the only method I use.

          Adding logging (error + access) and handling more complex situations is actually far less complicated than the guide I provided. Logging can be added with a few lines, and caching can be done with a few location blocks before the primary / block in my setup.

          I'm working on packing it all up in to a quick, easy-to-use bash script (something I've had on the docket for a while) to simplify things a little more.

          • @jtittle

            What do you think about changing nginx's (I believe it's nginx) account for each user so that each user still owns their own files? Is it worth doing? Because my only issue is that easy user can't modify their own websites now! Other than that I FINALLY have LEMP running Wordpress and wow it worked well!

To help narrow this down I think it's something in Linux that's the issue. As when I installed the plugin ssh sftp support everything is working beautifully. Ideally I wouldn't need this though :)

@danfoote104227

If you've created a new user:group and changed the ownership of all files and directories to the newly created user, then set all directories with a CHMOD 0755 and files with a CHMOD of 0644, you really shouldn't need a more complex setup. When a user owns the files and directories, it should be able to access them as needed without the use of a plugin or SSH keys.

i.e.

sudo useradd -d /var/www/html exampleuser \
&& chown -R exampleuser:exampleuser /var/www/html

Running the following command will recursively change all directories to CHMOD 0755:

find /var/www/html -type d -exec chmod 755 {} \;

Then, we'll handle files by setting the CHMOD to 0644

find /var/www/html -type f -exec chmod 644 {} \;

Using plugins and setting SSH keys for SFTP uploads seems to be overkill and the above is a far more simple solution that I've used in the past without any issues.

  • Hmm, the only difference in our setups was that I have the folder and all files owned by wp-user:www-data (I thought this was necessary, but I changed it to how you have it and the site still functions... the plugin does in fact fix it though. I just don't like that solution...

    Thank you very much for your entire setup though it will be a useful reference.

  • Just to clarify, you're referring to adding:

    define(‘FS_METHOD’, ‘direct’);

    to the wp-config.php file and letting apache write the files?

    • @danfoote104227

      When the user owns the files and directories, you shouldn't need the lines that define the method you wish to use (i.e. define()) as the web server and PHP should handle it as the user should be able to modify directory structure and files as needed.

      So what I'd do is remove or comment out the define() calls so that they are no longer being used. From there, try disabling that SSH plugin and then installing a theme or plugin without any of the added material from the guide.

      • @jtittle

        When doing that it presents me with a screen that asks for

        Connection Information
        
        To perform the requested action, WordPress needs to access your web server. Please enter your FTP or SSH credentials to proceed. If you do not remember your credentials, you should contact your web host.
        

@danfoote104227

From a security standpoint, each user should always have their own account.

What you're talking about is changing the user that PHP-FPM runs as, and yes, you can and should do that. You'll want to look in:

/etc/php/7.1/fpm/pool.d/

By default, there's only a single file in that directory and that's www.conf which uses www-data as the default user. To setup a PHP-FPM instance for each user, you'd simply copy that file to a new one and change the configuration within it.

For example, let's say we have user1, user2, user3. In the above directory, create:

user1.conf
user2.conf
user3.con

by simply copying the existing www.conf to a new file. The command below creates the 3 new files we need for this example.

cp /etc/php/7.1/fpm/pool.d/www.conf /etc/php/7.1/fpm/pool.d/user1.conf \
&& cp /etc/php/7.1/fpm/pool.d/www.conf /etc/php/7.1/fpm/pool.d/user2.conf
&& cp /etc/php/7.1/fpm/pool.d/www.conf /etc/php/7.1/fpm/pool.d/user3.conf

Now, you'd simply create directories for each user and then a new user account for each:

sudo mkdir -p /home/{user1,user2,user3} \
&& sudo useradd -d /home/user1 user1 \
&& sudo useradd -d /home/user2 user2 \
&& sudo useradd -d /home/user3 user3

Now we need to edit our newly created PHP-FPM configuration files and change a few specific values before we restart PHP-FPM. The lines you want to look at changing are:

[www]
user = www-data
group = www-data

and

listen = 127.0.0.1:9000

In the first, change [www] to the username (i.e [user1] ...). You'll then set the user and group to the same username. Finally, increase the port # by one (i.e. 9000 becomes 9001, 9002, etc).

The reason we need to increase the port is because we can't have two users listening in on the same port. Yes, it's really that simple :-).

Now, once all 3 configuration files have been modified, restart PHP-FPM.

sudo service php7.1-fpm restart

Now, the biggest change is going to be how I setup NGINX in the guide I provided you with. If you look in this file:

/etc/nginx/config/php/php-fpm.conf

You'll see where I defined the port that PHP-FPM connects on for that account. You'll need to copy this file in to each server block instead of including it and then change the port. So what you'd end up with is a server block that looks like the below for each account instead of the slimmer one in that guide.

server {
    listen                                          80;
    server_name                                     yourdomain.com www.yourdomain.com;
    root                                            /home/yourdomain/htdocs/public;
    index                                           index.php index.html index.htm;

    location / {
        try_files $uri $uri/ =404;
    }

    location ~ [^/]\.php(/|$) {
        fastcgi_split_path_info ^(.+?\.php)(/.*)$;
        fastcgi_pass   127.0.0.1:9000;
        fastcgi_index  index.php;
        fastcgi_param  SCRIPT_FILENAME    $request_filename;

        fastcgi_connect_timeout 60;
        fastcgi_send_timeout 180;
        fastcgi_read_timeout 180;
        fastcgi_buffer_size 512k;
        fastcgi_buffers 512 16k;
        fastcgi_busy_buffers_size 1m;
        fastcgi_temp_file_write_size 4m;
        fastcgi_max_temp_file_size 4m;
        fastcgi_intercept_errors off;

        fastcgi_param  PATH_INFO          $fastcgi_path_info;
        fastcgi_param  PATH_TRANSLATED    $document_root$fastcgi_path_info;

        fastcgi_param  QUERY_STRING       $query_string;
        fastcgi_param  REQUEST_METHOD     $request_method;
        fastcgi_param  CONTENT_TYPE       $content_type;
        fastcgi_param  CONTENT_LENGTH     $content_length;

        fastcgi_param  SCRIPT_NAME        $fastcgi_script_name;
        fastcgi_param  REQUEST_URI        $request_uri;
        fastcgi_param  DOCUMENT_URI       $document_uri;
        fastcgi_param  DOCUMENT_ROOT      $document_root;
        fastcgi_param  SERVER_PROTOCOL    $server_protocol;
        fastcgi_param  REQUEST_SCHEME     $scheme;
        fastcgi_param  HTTPS              $https if_not_empty;
        fastcgi_param  HTTP_PROXY         "";

        fastcgi_param  GATEWAY_INTERFACE  CGI/1.1;
        fastcgi_param  SERVER_SOFTWARE    nginx/$nginx_version;

        fastcgi_param  REMOTE_ADDR        $remote_addr;
        fastcgi_param  REMOTE_PORT        $remote_port;
        fastcgi_param  SERVER_ADDR        $server_addr;
        fastcgi_param  SERVER_PORT        $server_port;
        fastcgi_param  SERVER_NAME        $server_name;

        fastcgi_param  REDIRECT_STATUS    200;

    }
}

The only line in the PHP block that you need to change is fastcgi_pass 127.0.0.1:9000;. All you're doing here is making sure the ports match up.

Once you have your 3 server blocks, 3 PHP-FPM configuration files, and you're set:

nginx -s reload

NOTE: You could simply copy that file to another 2 files to make 3 and just modify the include line. This would probably be better down the line to reduce clutter, but for show, I've simply pasted the contents in to the server block.

  • @jtittle

    Alright one last question on this thread rofl. It's a small thing. I just set up the second website as instructed and everything works perfectly EXCEPT (I use Wordpress 99% of the time) any other page in the site, including wp-admin. I think it's because I used pretty links for this site I imported. I Googled how to allow URL rewrite but shoot that didn't work! (the solution was to change the

    try_files $uri $uri/ =404;
    chang to
    try_files $uri $uri/ /index.php?$args;
    

    This allowed me to pull up other pages in the site, but still no luck getting the admin page to show up, so I reverted the change for now.

    • Ignore that. I tailed the log files for nginx. Saw PHP errors. With a little Googling my outdated Wordpress didn't support 7.1! I was planning on updating after moving but that was what caused this problem haha.

      • @danfoote104227

        NGINX does require a little more customizing when it comes to WordPress and their URI structure (from time to time). The strange thing is that WordPress enforces one way and not all plugins follow it, thus you often have you're primary rewrite and then a secondary and in some cases, even a tertiary.

        Once updated, let me know if there's anything that isn't working and we'll get that sorted!

        • @jtittle
          So I can't figure out how I broke it again :P

          I upgraded my droplet because I was finally getting things rolling. Now every site refuses to connect and I don't think nginx is running. I used command line to power off, then shut down the droplet, then upgraded which I'm 99% sure is exactly how you do it?

          nginx -s reload
          

          is giving me the error

          nginx: [error] open() "/etc/nginx/pid/nginx.pid" failed (2: No such file or directory)
          
          • @danfoote104227

            If you restarted the VPS, you'll need to run just nginx which will start NGINX up and create the pid file that's missing.

            You only want to run the reload command when you add new configuration that NGINX needs to be aware of. So, for instance, when you change the main http block in nginx.conf, make changes to a server block for one of your sites, or when you simply want to refresh everything, thus ensuring all changes are loaded.

            My quick setup script doesn't include an init or systemd script, hence why you need to run nginx after restarting. There are a few floating around, though I plan on adding that to the configuration as well.

Have another answer? Share your knowledge.