Problem/Motivation
Right now (10.6.x and i assume 11.x) allows to run cron through the "run cron" UI button when the site is with the maintenance mode enabled or disabled.
However, cron can also be run through one certain url (/cron/{key}) defined in system.cron on core/modules/system/system.routing.yml
If it is needed to run cron using the mentioned URL under certain conditions (eg: a cronjob that can only call the url and not use drush cron instead), the operation fails if the site is under maintenance mode (and therefore the cron can only be executed manually)
This can cause sites under maintenance not fully operate if they still need to execute certain cron tasks and relying on the server cronjob, as the url will not be reachable, as the system is designed like that (quoting @berdir on slack:
there is no cron.php anymore, it's a regular route in D8+, and a regular route is subject to the maintenance request subscriber unless that has explicit rules on excluding something. most likely, nobody thought about that during the conversion.
)
Steps to reproduce
- call from a browser the cron url -> cron is executed
- set maintenance mode on
- call the same url again -> cron is not executed
- trigger cron from the UI as admin -> cron is executed
Proposed resolution
The mentioned defined route has one custom access_check set in \Drupal\system\Access\CronAccessCheck that is only used there.
The CronAccessCheck class has the following piece of code that is what is blocking cron execute it during maintenance mode:
elseif (\Drupal::state()->get('system.maintenance_mode')) {
\Drupal::logger('cron')->notice('Cron could not run because the site is in maintenance mode.');
return AccessResult::forbidden()->setCacheMaxAge(0);
}
in addition, the route needs to allow being called during maintenance mode, so the next change is also needed:
_maintenance_access: TRUE
under options: in the system.cron route (system.routing.yml)
After those changes, the cron url is allowed to be executed also during cron
The first change is valid for 11.x and 10.6.x but the second differs between versions, as the route is located in different lines of the same file (so backporting is not automatic)
Many thnks in advance
Issue fork drupal-3613439
Show commands
Start within a Git clone of the project using the version control instructions.
Or, if you do not have SSH keys set up on git.drupalcode.org:
Comments
Comment #2
shank115 commentedCan confirm the described behavior in current core (checked against 10.x/11.x).
One thing I'd be cautious about with the proposed fix: the cron URL is only protected by the cron key, not a real login — so anyone who has that key (even if they're not an admin) could trigger cron this way. Maintenance mode is often used while doing sensitive things like updates, so it seems possible the maintenance-mode check was added on purpose, to stop cron from running at a bad time via that key-only route. I'm not 100% sure that was the original reasoning, but it seems worth double-checking before just removing the check, in case there's a reason behind it we're not seeing.
Comment #3
gorkagr commentedThat's why i asked also first on slack (https://drupal.slack.com/archives/C079NQPQUEN/p1784820639464479)
But the cron can be executed manually during maintenance, and might be that it runs automatically while doing an update, because as far as i know, cim or updb do not pause cron, correct?
Comment #6
charlliequadros commentedI made the change and tested everything. Everything seems to be working correctly, and the solution makes sense to me. Please let me know if there is anything else I can do.
Comment #7
charlliequadros commentedComment #8
smustgrave commentedThink this needs sub-maintainer sign off not 100% we can just fully remove this.
Comment #9
gorkagr commentedAfter checking the drush docs on https://www.drush.org/13.x/cron/#avoiding-maintenance-mode, there is a section called Avoiding Maintenance mode and contains a link to a slack post talking about cron and maintenance mode as well.
So we should (i think) consider to unify when cron is executed, as i think there is now the following cases:
- Maintenance mode ON - run cron manually allowed
- Maintenance mode ON - run cron through URL not allowed (and that this issue is proposing to change)
- Maintenance mode ON - run cron through drush depends on how the command is called
- Maintenance mode OFF - all cases allowed
if executing cron during maintenance mode is smth to be avoided (see slack or cron docs), then maybe this issue can be restored and change the code to disallow cron in maintenance mode instead.
Leave open to discussion
Comment #10
mstrelan commentedReviewing as system module maintainer, since the route and access check live there. The underlying question, how maintenance mode should apply to all cron triggers, belongs to the cron subsystem, which has no maintainer, so I'm asking for framework manager review.
The maintenance-mode check in
CronAccessCheckis intentional. It dates back to D7'scron.phpand stops externally scheduled cron from running while a site is mid-deploy (new code, old schema). Removing it would take away that protection from sites that use a crontab + maintenance mode during deploys.#9 I think the fix goes the other way: unattended triggers (cron URL, automated_cron) should be skipped in maintenance mode, while manual triggers (the admin button,
drush cron) should keep working. The cron URL already does this, automated_cron does not, becauseAutomatedCron::onTerminate()doesn't check for maintenance mode.Perhaps instead we should fix automated_cron so it doesn't run in maintenance mode?