Tag Archives: cron

Certbot: auto-renew LetsEncrypt certificate on cron

Certificates renewal can be difficult to automate leading to errors that will mark the website as “Insecure”.

Here’s how to automate certificate renewal on CentOS 7 with nginx as webserver:

su -
(root password)
crontab -e

And then add to the crontab these lines pressing A to edit:

37 02 * * * /usr/local/sbin/certbot-auto renew
39 02 * * * /usr/bin/systemctl reload nginx

Every day at 02.37 the certificate will be asked for renewal. Two minutes later nginx will be restarted.

After you’ve typed these lines, type:

:wq

To write and quit. You’ll get this message:

crontab: installing new crontab

Other Linux distributions

If you’re using a different Linux OS you can locate certbot-auto using the following command:

whereis certbot-auto

And then you can use it on the crontab.

You’ve also to use the alternative of systemctl for your system to refresh the certificates on the webserver.

If you’ve to do get your first certificate, here you can get more information about how to install free Let’s Encrypt certificates on nginx.

When auto-renew certificates

Since certificates lasts for about three months, you can tell crontab to run this every week instead every day.

To do so, change the lines on crontab like this:

37 02 * * 0 /usr/local/sbin/certbot-auto renew
39 02 * * 0 /usr/bin/systemctl reload nginx

Adding the 0 on the 5th position will tell crontab to run the command every Sunday at 2 AM (2nd position) and 37 minutes (1st position).

If you find this syntax difficult you can use crontab.guru to easily generate the crontab.

Advertisements

Clear Varnish cache via PHP: a Drupal 7 proof of concept

Using Varnish as reverse proxy or proxy is an useful approach to reduce the load of webservers like Apache.

In Drupal 7 I’ve to clear the varnish cache of a specific domain when Drupal caches are globally cleared. Drupal has the right hook invoked when cache are cleared:

function clearcachevarnish_flush_caches() {
  $filename = '/var/www/varnishdomains2cleardir/varnishdomains2clear';
  // each domain on a separate line: append to the end of the file
  $myfile = fopen($filename, "a");
  $h = $_SERVER['HTTP_HOST'];
  $txt = $h . "\n";
  fwrite($myfile, $txt);
  fclose($myfile);
  drupal_set_message('Varnish cache queued to be cleared. Please wait 1 minute before checking.');
  // no cache table should be cleared
  return array();
}

Now this piece of code simply adds the current domain to a ASCII text file on /var/www/varnishdomains2cleardir/varnishdomains2clear.

Preparing the file to the write

On CentOS you have to add /var/www/varnishdomains2cleardir to the httpd-writable directories list using:

mkdir /var/www/varnishdomains2cleardir;
chcon -v --type=httpd_sys_content_t /var/www/varnishdomains2cleardir;
chown myuser:mygroup /var/www/varnishdomains2cleardir;
chmod -R 777 /var/www/varnishdomains2cleardir;
touch /var/www/varnishdomains2cleardir/varnishdomains2clear;

Now the empty file is ready to be written by your hook_flush_caches() implementation. Now enable the clearvarnishcache module and clear the cache to write the current domain name to the file.

The clear varnish cache script

To clear the varnish cache you usually have to be logged as root using the command varnishadm. Here a script that will read the domains file written above, clear the varnish cache for that domain and then remove the domains lines.

#!/bin/bash
callinguser=`whoami`
if [ "root" != "$callinguser" ]
then
 echo "Only root can run this command."
 exit 1
fi
cd /path/to/clear/cache/command/

date=`date +%Y-%m-%d_%H:%M:%S`

# check lock
# prevent the script from being run more than once
if [ -f /tmp/clearcachevarnish-lock ]; then
echo "Script clearcachevarnish is already running. You can rm /tmp/clearcachevarnish-lock to break the lock manually."
exit 1
fi
touch /tmp/clearcachevarnish-lock
dominidapulire=`less /var/www/varnishdomains2cleardir/varnishdomains2clear`
while [[ ! -z $dominidapulire ]]
do
 dominio=$(echo "$dominidapulire" | sed -n '$p')
 echo $dominio
 dominidapulire=$(echo "$dominidapulire" | sed '$d')
 if [ "" != "$dominio" ]
 then
 varnishadm -T 127.0.0.1:6082 -S /etc/varnish/secret ban req.http.host == "$dominio"
 echo "varnish cleared on $dominio"
 fi
done
# remove all domains lines
truncate --size 0 /var/www/varnishdomains2cleardir/varnishdomains2clear

# remove lock
rm /tmp/clearcachevarnish-lock

Make this script as executable .sh file using chmod a+x on it. If you run the bash script, varnish cache for files on the domains list will be cleared. It’s not so useful when using the Drupal UI so we should schedule this task periodically, e.g. every minute.

Scheduling the varnish clear cache

Here the crontab entry for execute the script every minute:


* * * * * root /path/to/clear/cache/command/clearcachevarnish.sh

The steps

  1. User clear Drupal cache
  2. hook_flush_caches() is invoked: the domains list file is written
  3. clear varnish cache script is launched by root every minute
  4. for each domain in the list, varnish cache is cleared

This is the end of this proof of concept. The code wasn’t tested against attacks so please comment if you have any suggestion to improve it. I’m not very fond of the idea of a php script writing something read by a bash script but this is the less problematic solution I found for this case.

Cron cannot run on Drupal: the drupal_goto() case

Sometimes you want to redirect a page to another on drupal. You can do this using a simple function called drupal_goto().

On few sites I’ve enabled the PHP filter module and then created a new page with PHP code input format with drupal_goto(‘node/2’) to redirect the current page to a specified node. Bad idea.

I’ve noticed that, after this change, cron.php operations failed, if you have Search module enabled. On cron new contents are indexed by the Search module: when it got my PHP page, it tries to index it but suddenly is redirected to another. You can also found an error like “Maximum function nesting level of ‘100’ reached” on php error log, symptom of an indexing blocked by pages with drupal_goto inside.

Solution:

  1. Comment all drupal_goto() instruction in your site within pages.
  2. Use an alternative method to redirect from a node to another.
  3. Run cron from Status Report page: you can adjust indexed content per cron on Search configuration page (admin/settings/search on 6.x)

You can add a new block with PHP code inside or (better) create a new module for this simple block (with a simple PHP switch statement as content), displaying it only on specified pages (on the bottom of block configuration). But if you create a PHP block via UI, and you put that block on every page, your site could be loop, so creating a module is the cleanest and secure way (if something go wrong, you can delete your module from the codebase and correct it). You can also find some contrib modules for redirect on drupalmodules.com.

See also:

Fatal error: Maximum function nesting level of ‘100’ reached, aborting!