Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

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

Open
whereisaaron opened this issue Sep 6, 2022 · 1 comment

Comments

@whereisaaron
Copy link

whereisaaron commented Sep 6, 2022

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
Copy link

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

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants