Category: Web Servers

  • Mastodon discoverability on your domain using the nginx location directive

    tl;dr: Puts the .well-known/webfinger content in your server config instead of the filesystem.

    This setup is inspired by Maarten Balliauw‘s Mastodon on your own domain without hosting a server. Please read the linked post for details. The general idea is you can create a special file on your website that allows people to find you on Mastodon by searching for @you@yourdomain.tld even though you don’t run an instance on your domain.

    Here’s my twist: What if you can’t or don’t want to add this file directly to the filesystem where your web content lives? Maybe your site is running a CMS that precludes adding arbitrary files. Or you just don’t want to forget to preserve the file between software upgrades and reinstalls. Whatever your reason, you can do this by putting the file content inside of a location directive in your nginx config instead.

    These instructions assume you have a working knowledge of nginx configuration and server administration.

    1. In a browser navigate to https://<your mastodon server>/.well-known/webfinger?resource=acct:<your account>@<your mastodon server>
    2. Select and copy the json text that appears on the page.
    3. Edit the nginx config file that contains the server block for your domain.
    4. Add a new location directive like below, replacing your-code-here with the json that was copied in step 2.
      location /.well-known/webfinger {
           return 200 'your-code-here'; 
      }
    5. Save your config and restart nginx.
  • Restrict Access to WordPress with Nginx and GeoIP

    The goal of this post is to harden your WordPress dashboard by preventing logins from countries where you know you will never be connecting. Since brute-force login attempts may still originate from an allowed country, it would be wise to combine this with other tools like fail2ban or one of the numerous plug-ins that add login rate-limiting restrictions to WordPress.

    This example targets Nginx on CentOS 7 using PHP 7 from the Remi repo. Settings may vary depending on your exact setup, so watch out for differences from your config, and back up your /etc/nginx first!

    First make sure you keep an up-to-date GeoIP database by running a script like the example below via a nightly cron job.

    #!/bin/bash
    
    /bin/curl https://geolite.maxmind.com/download/geoip/database/GeoLiteCountry/GeoIP.dat.gz -o /tmp/GeoIP.dat.gz
    
    /bin/gunzip -c /tmp/GeoIP.dat.gz > /etc/nginx/GeoIP.dat
    
    /bin/rm -f /tmp/GeoIP.dat.gz

    Make sure the GeoIP module is installed:

    yum install nginx-mod-http-geoip

    Make sure the module is loaded in /etc/nginx/nginx.conf:

    load_module modules/ngx_http_geoip_module.so;

    Inside of http { } in nginx.conf, add this, modifying country and default as desired:

    geoip_country /etc/nginx/GeoIP.dat;
    map $geoip_country_code $allowed_country {
       default no;
       US yes;
    }

    And finally add the necessary location statement in your virtual host’s server { }. In this example I’m using PHP 7.1 from the Remi repo, so your configuration may vary.

    location ~ ^/(wp-admin|wp-login.php) {
     try_files $uri $uri/ /index.php?$args;
     # try_files $uri =404;
     fastcgi_pass unix:/var/run/php-fpm/php71-fpm.sock;
     fastcgi_index index.php;
     fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
     include fastcgi_params;
     index index.html index.htm index.php;
     if ($allowed_country = no) {
     return 444;
     }
    }
  • Automatically Update WordPress, Themes, and Plugins using WP-CLI

    WordPress is a hugely popular blog/CMS platform, but with widespread adoption comes risk: It is a common target for hackers, exploits, etc. Accordingly, you should make sure it gets regular updates.

    WordPress has a built-in update mechanism but this also requires that its PHP files be writable by the web server, introducing a new set of security risks.

    Luckily there is another option. Instead you can use a command-line tool called WP-CLI, which enables us to script WordPress updates.

    These instructions will outline the steps necessary to install WP-CLI, create a script to update multiple sites at once, and install that script as a cron job to ensure updates happen on a regular schedule.


    Before You Begin

    As with any WordPress maintenance tasks, I recommend making regular backups of your database and files.

    For this process to succeed, you’ll need to run your script as a user who has permission to modify the WordPress files. This could be your regular user account, but you might also want to create a dedicated user such with a name like ‘scripts’, and give it write permissions to your WordPress files. It is not recommended to run this as root.


    1. Install WP-CLI
      Install WP-CLI (adapted from http://wp-cli.org/#installing)

      curl -O https://raw.githubusercontent.com/wp-cli/builds/gh-pages/phar/wp-cli.phar
      chmod +x wp-cli.phar
      sudo mv wp-cli.phar /usr/local/bin/wp
    2. Test WP-CLI
      Run as a user who has write privileges to your WordPress site. If everything works you should get a series of “Success” messages, and/or a list of updated items.

      cd /var/www/html   # replace with path to your site 
      /usr/local/bin/wp core update
      /usr/local/bin/wp core update-db
      /usr/local/bin/wp theme update --all
      /usr/local/bin/wp plugin update --all
    3. Create an Update Script
      Use your favorite text editor to create a new shell script. In that script, put the following code:

      #!/bin/bash
      
      # Absolute paths of WordPress sites. Space-separated.
      sites="/var/www/html/site1 /var/www/html/site2 /var/www/html/site3"
      
      for site in $sites; do
      
      echo $site
      
      /usr/local/bin/wp core update --path=$site --quiet
      /usr/local/bin/wp core update-db --path=$site --quiet
      /usr/local/bin/wp theme update --all --path=$site --quiet
      /usr/local/bin/wp plugin update --all --path=$site --quiet
      
      done
    4. Make The Script Executable
      chmod 700 wp-update
    5. Test The Update Script
      ./wp-update

      If everything works you’ll see a series of “Success” messages, and/or a list of updated items. If you see errors, double-check that your current user has permission to write to the WordPress site directories.

    6. Install Cron Job
      Make sure you’re still logged in as a user who has write permissions for the WordPress site directories.

      crontab -e

      Now create a new cron entry like this one, including the correct path to your update script. In this example it will run every day at 2:30am.

      30 2 * * * /home/scripts/bin/wp-update

      Close and save your crontab file.

    7. If everything worked correctly, your WordPress sites will now auto-update every night.
  • File ownership considerations with Nginx and php-fpm

    I recently switched my CentOS 7 web server over to Nginx and php-fpm.

    From my experience with Apache I assumed that PHP scripts would be executed by the same user the web server is running as — ‘nginx’ in this case. But this could no longer be taken for granted since php-fpm is a separate process from the web server.

    In my configuration php-fpm was actually running as the ‘apache’ user. This meant any files that need to be writable by PHP scripts should still be owned by that user or group rather than ‘nginx’.

    A common scenario where this matters is if your users need to be able to install WordPress updates, Plugins, or Themes via the browser without entering additional credentials. In order for this to work, the web server (or in this case, php-fpm) must be able to write to the files in question.

    If you are wrestling with file permissions, or are unsure of the correct permissions to set in this scenario, be sure to confirm which user and group are specified in /etc/php-fpm.d/www.conf

    # grep "^user\|^group" /etc/php-fpm.d/www.conf 
    user = apache
    group = apache

    Or you can check the actual running process with pstree:

    $ pstree -ua | grep "nginx\|php"
      |-nginx
      |   |-nginx,nginx
      |   |-nginx,nginx
      |   |-nginx,nginx
      |   |-nginx,nginx
      |   |-nginx,nginx
      |   |-nginx,nginx
      |   |-nginx,nginx
      |   `-nginx,nginx
      |-php-fpm
      |   |-php-fpm,apache              
      |   |-php-fpm,apache              
      |   |-php-fpm,apache              
      |   |-php-fpm,apache              
      |   |-php-fpm,apache              
      |   |-php-fpm,apache              
      |   `-php-fpm,apache