icing / mod_md

Let's Encrypt (ACME) in Apache httpd
https://icing.github.io/mod_md/
Apache License 2.0
334 stars 28 forks source link

Sample setuid command to enable 'MDMessageCmd renewed' to cause a graceful restart of httpd #298

Closed whereisaaron closed 21 hours ago

whereisaaron commented 2 years ago

Greatly enjoying working with mod_md. It works great. Four features I wish it also had are:

  1. Ability to automatically restart after issuing/renewing a certificate
  2. Pre-flighting ACME challenges before asking ACME server to test the challenge
  3. Clean up of orphaned certificate directories in domains and staging
  4. Ability to detect domains certificates have been updated when in a shared filesystem

This GIST aims to address (1) buy providing a setuid command+script you can install to enable httpd to automatically restart after a certificate is issued or installed.

https://gist.github.com/whereisaaron/d4e94bac59cf01ca213f50756fe1155c

Other solutions involve using external services like crond to watch or do scheduled restarts. However this solution enables a single httpd process in a single container to handle it. And it enables mod_md to load newly issued certificates without delay. It consists of a setuid command that invokes a bash script as root.

Appreciate any suggestions to improve security or robustness.

MDMessageCmd /usr/local/sbin/md_event
/*
 * Execute md_event.sh
 *
 * 'md_event.sh' is an event handler for apache2 'mod_md' events,
 * including setting up ACME challenge responses for certificates,
 * and after renewing certificates.
 *
 * This binary simply executes 'md_event.sh' in a fixed location,
 * passing any command arguments.
 *
 * By making the 'md_event' binary compiled from this code 'setuid'
 * the 'mod_md' bash script will we run as 'root', which enables
 * the script to restart apache2, enabling it to load any new
 * or renewed certificiates mod_md has in the 'staging' directory.
 *
 * The 'md_event' binary and 'md_event.sh' script must be protected
 * from tampering or else your attacker will thank you later.
 *
 */

#define _GNU_SOURCE
#include <stdio.h>
#include <unistd.h>
#include <errno.h>

const char script_path[] = "/usr/local/sbin/md_event.sh";

int main (int argc, char *argv[]) {

    // Set uid to 'root'
    if (setuid(0) != 0) {
        perror("Failed to change user to root");
        return errno;
    }

    // Exec the script (using its hash-bang interpretor)
    execv(script_path, argv);
    // execv() will only return if an error occurred
    char *message;
    asprintf(&message, "Failed to exec %s", script_path);
    perror(message);
    return errno;
}
#!/bin/bash

# Log the invocation and effective user id
echo "Running as $(id -nu $EUID): md_event.sh $*"

#
# Restart apache2 gracefully
# This enable mod_md to load newly renewed certificate
# This script needs to be run as root
#

# We only care about after a certificate has been renewed
if [[ "$1" != "renewed" ]]; then
  exit 0
fi

# Debian default 'nofiles' ulimit is to set 8192 but AWS ECS Fargate hard limit is 4096
# This env var will change command run by '/usr/sbin/apachectl'
export APACHE_ULIMIT_MAX_FILES="ulimit -n 4096"

echo "Domain '$2' has been renewed, restarting apache2"
apache2ctl configtest && apache2ctl graceful

result=$?
if (( $result == 0 )); then
  echo "Successful restart of apache2 after renewal of '$2'"
else
  echo "Failed restart of apache2 after renewal of '$2'"
fi

# No-zero exit will mean mod_md will keeping make this same call again
# until zero is returned, which increasing back-off periods.
exit $result

# end

INSTALL

sudo gcc md_event.c -o /usr/local/sbin/md_event
sudo chmod u+s /usr/local/sbin/md_event
sudo cp md_event.sh /usr/local/sbin/
realsimix commented 1 year ago

Hi,

I find this idea very interesting and will maybe use it the restart services like postfix after updating certificates using mod_md. What are the advantages of using this md_event helper instead of using something like sudo to do the restarts?

Thanks, Simon

icing commented 21 hours ago

Closed as being stale.