From dfe49baea5403064f7e35ba3a61179d3faecfeb6 Mon Sep 17 00:00:00 2001 From: Daniel Baumann Date: Mon, 15 Apr 2024 22:24:06 +0200 Subject: Adding upstream version 0.8.4. Signed-off-by: Daniel Baumann --- Makefile | 49 +++++++ molly-guard.xml | 344 ++++++++++++++++++++++++++++++++++++++++++++++++ rc | 19 +++ run.d/10-print-message | 22 ++++ run.d/30-query-hostname | 102 ++++++++++++++ shutdown.in | 152 +++++++++++++++++++++ 6 files changed, 688 insertions(+) create mode 100644 Makefile create mode 100644 molly-guard.xml create mode 100644 rc create mode 100755 run.d/10-print-message create mode 100755 run.d/30-query-hostname create mode 100755 shutdown.in diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..afb034d --- /dev/null +++ b/Makefile @@ -0,0 +1,49 @@ +PREFIX?=/usr +cfgdir?=/etc/molly-guard +libdir?=$(PREFIX)/lib +sbindir?=$(PREFIX)/sbin +REALPATH?=$(libdir)/molly-guard + +all: molly-guard.8 shutdown + +%.8: DB2MAN=/usr/share/sgml/docbook/stylesheet/xsl/nwalsh/manpages/docbook.xsl +%.8: XP=xsltproc -''-nonet +%.8: %.xml + $(XP) $(DB2MAN) $< + +man: molly-guard.8 + man -l $< +.PHONY: man + +clean: + rm -f shutdown + rm -f molly-guard.8 +.PHONY: clean + +shutdown: shutdown.in + sed -e 's,@cfgdir@,$(cfgdir),g;s,@REALPATH@,$(REALPATH),g' $< > $@ + +install: shutdown molly-guard.8 + mkdir -m755 --parent $(DESTDIR)$(libdir)/molly-guard + install -m755 -oroot -oroot shutdown $(DESTDIR)$(libdir)/molly-guard/molly-guard + + mkdir -m755 --parent $(DESTDIR)$(sbindir) + ln -s $(libdir)/molly-guard/molly-guard $(DESTDIR)$(sbindir)/poweroff + ln -s $(libdir)/molly-guard/molly-guard $(DESTDIR)$(sbindir)/halt + ln -s $(libdir)/molly-guard/molly-guard $(DESTDIR)$(sbindir)/reboot + ln -s $(libdir)/molly-guard/molly-guard $(DESTDIR)$(sbindir)/shutdown + ln -s $(libdir)/molly-guard/molly-guard $(DESTDIR)$(sbindir)/coldreboot + ln -s $(libdir)/molly-guard/molly-guard $(DESTDIR)$(sbindir)/pm-hibernate + ln -s $(libdir)/molly-guard/molly-guard $(DESTDIR)$(sbindir)/pm-suspend + ln -s $(libdir)/molly-guard/molly-guard $(DESTDIR)$(sbindir)/pm-suspend-hybrid + + mkdir -m755 --parent $(DESTDIR)$(cfgdir) + install -m644 -oroot -oroot rc $(DESTDIR)$(cfgdir) + cp -r run.d $(DESTDIR)$(cfgdir) \ + && chown root.root $(DESTDIR)$(cfgdir)/run.d && chmod 755 $(DESTDIR)$(cfgdir)/run.d + + mkdir -m755 --parent $(DESTDIR)$(cfgdir)/messages.d + + mkdir -m755 --parent $(DESTDIR)$(PREFIX)/share/man/man8 + install -m644 -oroot -groot molly-guard.8 $(DESTDIR)$(PREFIX)/share/man/man8 +.PHONY: install diff --git a/molly-guard.xml b/molly-guard.xml new file mode 100644 index 0000000..9c01f2f --- /dev/null +++ b/molly-guard.xml @@ -0,0 +1,344 @@ + +.
will be generated. You may view the +manual page with: nroff -man .
| less'. A +typical entry in a Makefile or Makefile.am is: + +DB2MAN=/usr/share/sgml/docbook/stylesheet/xsl/nwalsh/\ +manpages/docbook.xsl +XP=xsltproc -''-nonet + +manpage.1: manpage.dbk + $(XP) $(DB2MAN) $< + +The xsltproc binary is found in the xsltproc package. The +XSL files are in docbook-xsl. Please remember that if you +create the nroff version in one of the debian/rules file +targets (such as build), you will need to include xsltproc +and docbook-xsl in your Build-Depends control field. + +--> + + + martin f."> + krafft"> + + Apr 19, 2008"> + + 8"> + madduck@madduck.net"> + + molly-guard"> + + molly-guard"> + + Debian"> + GNU"> + GPL"> +]> + + + +
+ &dhemail; +
+ + 2008 + &dhusername; + + &dhdate; +
+ + &dhucpackage; + + &dhsection; + + + &dhcommand; + + guard against accidental shutdowns/reboots + + + + + shutdown + + - + + + + + + -- script_options + + + + halt + + - + + + + + + -- script_options + + + + reboot + + - + + + + + + -- script_options + + + + poweroff + + - + + + + + + -- script_options + + + + coldreboot + + - + + + + + + -- script_options + + + + pm-hibernate + + - + + + + + + -- script_options + + + + pm-suspend + + - + + + + + + -- script_options + + + + pm-suspend-hybrid + + - + + + + + + -- script_options + + + + + + DESCRIPTION + + &dhcommand; attempts to prevent you from accidentally shutting down + or rebooting machines. It does this by injecting a couple of checks + before the existing commands: coldreboot, + halt, reboot, + shutdown, poweroff, + pm-hibernate,pm-suspend + and pm-suspend-hybrid. + + Before &dhcommand; invokes the real command, all scripts in + /etc/molly-guard/run.d/ have to run and exit + successfully; else, it aborts the command. + run-parts(1) is used to process the directory. + + &dhcommand; passes any script_options to the + scripts, and also populates the environment with the following + variables: + + + MOLLYGUARD_CMD - the actual command + invoked by the user. + + MOLLYGUARD_DO_NOTHING - set to + if this is a demo-run. + + MOLLYGUARD_SETTINGS - the path to + a shell script snippet which scripts can source to obtain + settings. + + + &dhcommand; prints the contents of + /etc/molly-guard/messages.d/COMMAND or + /etc/molly-guard/messages.d/default to the console, + if either exists. This is due to + /etc/molly-guard/run.d/10-print-message. + + + + GUARDING SSH SESSIONS + + &dhcommand; was primarily designed to shield SSH connections. This + functionality (which should arguably be provided by the + openssh-server package) is implemented in + /etc/molly-guard/run.d/30-query-hostname. + + This script first tests whether the command is being executed from + a tty which has been created by + sshd. It also checks whether the variable + SSH_CONNECTION is defined. If any of these tests are + successful, test script queries the user for the machine's hostname, + which should be sufficient to prevent the user from doing something by + accident. + + You can pass the script option to + &dhcommand; to pretend that those tests succeeds. Alternatively, setting + ALWAYS_QUERY_HOSTNAME in + /etc/molly-guard/rc causes the script to + always query. + + The following situations are still UNGUARDED. If you can think of + ways to protect against those, please let me know! + + + running sudo within + screen or screen within + sudo; sudo eats the + SSH_CONNECTION variable, and + screen creates a new + pty. + executing those command in a remote terminal window, + that is a XTerm started on a remote + machine but displaying on the local X + server. + + + You have been warned. You can use the + switch to prevent anything + from happening, e.g. halt + --molly-guard-do-nothing. + + + + OPTIONS + + + --molly-guard-do-nothing + + + Cause &dhcommand; to print the command which would be executed, + after processing all scripts, instead of executing it. + + + + + + -h + --help + + + Display usage information. + + + + + + -V + --version + + + Display version information. + + + + + + + + SEE ALSO + + + shutdown + 8 + , + + halt + 1 + , + + reboot + 8 + , + + poweroff + 8 + . + + coldreboot + 8 + . + + pm-hibernate + 8 + . + + pm-suspend + 8 + . + + pm-suspend-hybrid + 8 + . + + + + + LEGALESE + + + &dhpackage; is copyright by &dhusername;. Andrew Ruthven came up with + the idea of using the scripts directory and submitted a patch, which + I modified a bit. + + + + This manual page was written by &dhusername; &dhemail;. + + + + Permission is granted to copy, distribute and/or modify this document + under the terms of the Artistic License 2.0 + + + +
diff --git a/rc b/rc new file mode 100644 index 0000000..ee1ff43 --- /dev/null +++ b/rc @@ -0,0 +1,19 @@ +# molly-guard settings +# +# ALWAYS_QUERY_HOSTNAME +# When set, causes the 30-query-hostname script to always ask for the +# hostname, even if no SSH session was detected. +#ALWAYS_QUERY_HOSTNAME=true + +# USE_FQDN +# When set, causes the 30-query-hostname script to ask for the fully-qualified +# hostname, rather than the short name +#USE_FQDN=true + +# ANSIBLE_SEARCH_STRING +# The string to search for in the broadcast message provided to shutdown +# set by Ansible. Used to detect if Ansible has initiated a reboot via +# the ansible.builtin.reboot module the default message is "Reboot initiated +# by Ansible" but can be changed by setting the "msg" parameter. This is +# a case insensitive search. +#ANSIBLE_SEARCH_STRING="ansible" diff --git a/run.d/10-print-message b/run.d/10-print-message new file mode 100755 index 0000000..0e19526 --- /dev/null +++ b/run.d/10-print-message @@ -0,0 +1,22 @@ +#!/bin/sh +# +# 10-print-message - print a (command-specific or default) message +# +# Copyright © Andrew Ruthven +# Copyright © martin f. krafft +# Released under the terms of the Artistic Licence 2.0 +# +# Prints either /etc/molly-guard/messages.d/$MOLLYGUARD_CMD +# or /etc/molly-guard/messages.d/default +# depending on whether the first exists. +# +set -eu + +MESSAGESDIR=/etc/molly-guard/messages.d + +for i in $MOLLYGUARD_CMD default; do + if [ -f "$MESSAGESDIR/$i" ] && [ -r "$MESSAGESDIR/$i" ]; then + cat $MESSAGESDIR/$i + exit 0 + fi +done diff --git a/run.d/30-query-hostname b/run.d/30-query-hostname new file mode 100755 index 0000000..da5c6b8 --- /dev/null +++ b/run.d/30-query-hostname @@ -0,0 +1,102 @@ +#!/bin/sh +# +# 30-ask-hostname - request the user to type in the hostname of the local host +# +# Copyright © 2006-2009 martin f. krafft +# Copyright © 2012 Ludovico Gardenghi +# Copyright © 2014 Josh Triplett +# Copyright © 2015 Francois Marier +# Copyright © 2017 Simó Albert i Beltran +# Copyright © 2023 Andrew Ruthven +# Released under the terms of the Artistic Licence 2.0 +# +set -eu + +ME=molly-guard + +# Skip if we're being run by configuration management. +[ $CONFIGURATION_MANAGEMENT -eq 1 ] && exit 0 + +# Walk up the process tree until PID 1 is reached or a process with 'sshd' in +# its /proc//cmdline is met. Return success if such a process is found. +is_child_of_sshd_or_mosh_server() { + pid=$$ + ppid=$PPID + # Be a bit paranoid with the guard, should some horribly broken system + # provide a strange process hierarchy. '[ $pid -ne 1 ]' should be enough for + # sane systems. + [ -z "$pid" ] || [ -z "$ppid" ] && return 2 + while [ $pid -gt 1 ] && [ $pid -ne $ppid ]; do + if egrep -q 'sshd|mosh-server|etterminal' /proc/$ppid/cmdline; then + return 0 + fi + pid=$ppid + ppid=$(grep ^PPid: /proc/$pid/status | tr -dc 0-9) + done + return 1 +} + +[ -f "$MOLLYGUARD_SETTINGS" ] && . "$MOLLYGUARD_SETTINGS" + +PRETEND_SSH=0 +for arg in "$@"; do + case "$arg" in + (*-pretend-ssh) PRETEND_SSH=1;; + esac +done + +# require an interactive terminal connected to stdin +test -t 0 || exit 0 + +# we've been asked to always protect this host +case "${ALWAYS_QUERY_HOSTNAME:-0}" in + 0|false|False|no|No|off|Off) + # only run if we are being called over SSH, that is if the current terminal + # was created by sshd. + command -v tty >/dev/null 2>&1 || exit 0 + PTS=$(tty) + if ! pgrep -f "^sshd.+${PTS#/dev/}\>" >/dev/null \ + && [ -z "${SSH_CONNECTION:-}" ] \ + && ! is_child_of_sshd_or_mosh_server; then + if [ $PRETEND_SSH -eq 1 ]; then + echo "I: $ME: this is not an SSH session, but --pretend-ssh was given..." >&2 + else + exit 0 + fi + else + echo "W: $ME: SSH session detected!" >&2 + fi + ;; + *) + echo "I: $ME: $MOLLYGUARD_CMD is always molly-guarded on this system." >&2 + ;; +esac + +case "${USE_FQDN:-0}" in + 0|false|False|no|No|off|Off) + HOSTNAME="$(hostname --short)" + ;; + *) + HOSTNAME="$(hostname --fqdn)" + ;; +esac + +sigh() +{ + echo "Good thing I asked; I won't $MOLLYGUARD_CMD $HOSTNAME ..." >&2 + exit 1 +} + +trap 'echo;sigh' 1 2 3 9 10 12 15 + +echo -n "Please type in hostname of the machine to $MOLLYGUARD_CMD: " +read HOSTNAME_USER || : + +HOSTNAME="$(echo "$HOSTNAME" | tr '[:upper:]' '[:lower:]')" +HOSTNAME_USER="$(echo "$HOSTNAME_USER" | tr '[:upper:]' '[:lower:]')" + +[ "$HOSTNAME_USER" = "$HOSTNAME" ] || sigh + +trap - 1 2 3 9 10 12 15 + +exit 0 diff --git a/shutdown.in b/shutdown.in new file mode 100755 index 0000000..3216933 --- /dev/null +++ b/shutdown.in @@ -0,0 +1,152 @@ +#!/bin/sh +# +# shutdown -- wrapper script to guard against accidental shutdowns +# +# Copyright © martin f. krafft +# Released under the terms of the Artistic Licence 2.0 +# +set -eu + +ME=molly-guard +VERSION=0.8 + +SCRIPTSDIR="@cfgdir@/run.d" + +CMD="${0##*/}" + +case "$CMD" in + halt|reboot|shutdown|poweroff|coldreboot|pm-hibernate|pm-suspend|pm-suspend-hybrid) + if ! EXEC=$(command -v "$CMD.no-molly-guard"); then + if ! EXEC=$(command -v "$CMD.no-molly-guard.usr-is-merged"); then + echo "E: not a regular file: $EXEC" >&2 + exit 4 + fi + fi + if [ "$EXEC" -ef /usr/lib/molly-guard/molly-guard ]; then + # Symlink forwards to ourselves. Resolve! + LINKTARGET=$(readlink "$EXEC") + if ! EXEC=$(command -v "$LINKTARGET.no-molly-guard"); then + if ! EXEC=$(command -v "$LINKTARGET.no-molly-guard.usr-is-merged"); then + echo "E: not a regular file $EXEC" >&2 + exit 4 + fi + fi + fi + if [ ! -x $EXEC ]; then + echo "E: not an executable: $EXEC" >&2 + exit 3 + fi + ;; + *) + echo "E: unsupported command: $CMD" >&2 + exit 1 + ;; +esac + +usage() +{ + cat <<-_eousage + Usage: $ME [options] [-- script options] + (shielding $EXEC) + + molly-guard's primary goal is to guard against accidental + shutdowns/reboots. $ME will run all scripts in $SCRIPTSDIR and only + invokes $EXEC if all scripts exited successfully. + + Specifying --molly-guard-do-nothing as argument to the command will + make $ME echo the command it would execute rather than actually + executing it. + + Options following the double hyphen will be passed unchanged to the + scripts. + + Please see molly-guard(8) for more information. + + The actual command's help output follows: + + _eousage +} + +CMDARGS= +SCRIPTARGS= +END_OF_ARGS=0 +DO_NOTHING=0 +for arg in "$@"; do + case "$arg" in + (*-molly-guard-do-nothing) DO_NOTHING=1;; + (*-help) + usage 2>&1 + ( exec bash -c 'exec -a "$0" "$@"' "$CMD" "$EXEC" --help 2>&1 ) + exit 0 + ;; + --) END_OF_ARGS=1;; + *\"*) + echo 'E: cannot use double-quotes (") in arguments' >&2 + exit 1 + ;; + *) + if [ $END_OF_ARGS -eq 0 ]; then + CMDARGS="${CMDARGS:+$CMDARGS }\"$arg\"" + else + SCRIPTARGS="${SCRIPTARGS:+$SCRIPTARGS }--arg \"$arg\"" + fi + ;; + esac +done + +do_real_cmd() +{ + if [ $DO_NOTHING -eq 1 ]; then + echo "$ME: would run: $EXEC $CMDARGS" + exit 0 + else + eval exec bash -c "'"'exec -a "$0" "$@"'"'" "'$CMD'" "'$EXEC'" "$CMDARGS" + fi +} + +if [ $DO_NOTHING -eq 1 ]; then + echo "I: demo mode; $ME will not do anything due to --molly-guard-do-nothing." >&2 +fi + +if [ -n "${MOLLYGUARD_CMD:-}" ]; then + do_real_cmd +fi + +MOLLYGUARD_CMD=$CMD; export MOLLYGUARD_CMD +MOLLYGUARD_DO_NOTHING=$DO_NOTHING; export MOLLYGUARD_DO_NOTHING +MOLLYGUARD_SETTINGS="@cfgdir@/rc"; export MOLLYGUARD_SETTINGS + +# pass through certain commands +case "$CMD $CMDARGS" in + (*shutdown\ *-c*|*halt\ *-w*|*halt\ *-f*|*reboot\ *-f*) + # allow canceling shutdowns, only write wtmp and force immediate halt + echo "I: executing $CMD $CMDARGS regardless of check results." >&2 + do_real_cmd + ;; +esac + +# Check for execution from configuration management tools that might use an +# interactive shell. +CONFIGURATION_MANAGEMENT=0 + +[ -f "$MOLLYGUARD_SETTINGS" ] && . "$MOLLYGUARD_SETTINGS" + +# Ansible uses an interactive shell, but by default puts Ansible in the +# broadcast message, we can look for that. Allow for it be changed per site. +ANSIBLE_SEARCH_STRING="${ANSIBLE_SEARCH_STRING:-ansible}" +if echo $CMDARGS | grep -qi $ANSIBLE_SEARCH_STRING; then + CONFIGURATION_MANAGEMENT=1 +fi + +export CONFIGURATION_MANAGEMENT + +for script in $(run-parts --test $SCRIPTSDIR); do + ret=0 + eval $script $SCRIPTARGS || ret=$? + if [ $ret -ne 0 ]; then + echo "W: aborting $CMD due to ${script##*/} exiting with code $ret." >&2 + exit $ret + fi +done + +do_real_cmd -- cgit v1.2.3