1
0
Fork 0

Adding upstream version 0.8.5.

Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
This commit is contained in:
Daniel Baumann 2025-06-21 08:01:25 +02:00
parent c44f6858f9
commit 47ba6c9762
Signed by: daniel.baumann
GPG key ID: BCC918A2ABD66424
6 changed files with 688 additions and 0 deletions

49
Makefile Normal file
View file

@ -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

344
molly-guard.xml Normal file
View file

@ -0,0 +1,344 @@
<?xml version='1.0' encoding='ISO-8859-1'?>
<!DOCTYPE refentry PUBLIC "-//OASIS//DTD DocBook XML V4.2//EN"
"http://www.oasis-open.org/docbook/xml/4.2/docbookx.dtd" [
<!--
Process this file with an XSLT processor: `xsltproc \
-''-nonet /usr/share/sgml/docbook/stylesheet/xsl/nwalsh/\
manpages/docbook.xsl manpage.dbk'. A manual page
<package>.<section> will be generated. You may view the
manual page with: nroff -man <package>.<section> | 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.
-->
<!-- Fill in your name for FIRSTNAME and SURNAME. -->
<!ENTITY dhfirstname "<firstname>martin f.</firstname>">
<!ENTITY dhsurname "<surname>krafft</surname>">
<!-- Please adjust the date whenever revising the manpage. -->
<!ENTITY dhdate "<date>Apr 19, 2008</date>">
<!-- SECTION should be 1-8, maybe w/ subsection other parameters are
allowed: see man(7), man(1). -->
<!ENTITY dhsection "<manvolnum>8</manvolnum>">
<!ENTITY dhemail "<email>madduck@madduck.net</email>">
<!ENTITY dhusername "martin f. krafft">
<!ENTITY dhucpackage "<refentrytitle>molly-guard</refentrytitle>">
<!ENTITY dhpackage "molly-guard">
<!ENTITY dhcommand "<command>molly-guard</command>">
<!ENTITY debian "<productname>Debian</productname>">
<!ENTITY gnu "<acronym>GNU</acronym>">
<!ENTITY gpl "&gnu; <acronym>GPL</acronym>">
]>
<refentry>
<refentryinfo>
<address>
&dhemail;
</address>
<copyright>
<year>2008</year>
<holder>&dhusername;</holder>
</copyright>
&dhdate;
</refentryinfo>
<refmeta>
&dhucpackage;
&dhsection;
</refmeta>
<refnamediv>
<refname>&dhcommand;</refname>
<refpurpose>guard against accidental shutdowns/reboots</refpurpose>
</refnamediv>
<refsynopsisdiv>
<cmdsynopsis>
<command>shutdown</command>
<arg choice="opt">
-<option>hV</option>
</arg>
<arg choice="opt">
<option>--molly-guard-do-nothing</option>
</arg>
<arg choice="opt">
-- <replaceable>script_options</replaceable>
</arg>
</cmdsynopsis>
<cmdsynopsis>
<command>halt</command>
<arg choice="opt">
-<option>hV</option>
</arg>
<arg choice="opt">
<option>--molly-guard-do-nothing</option>
</arg>
<arg choice="opt">
-- <replaceable>script_options</replaceable>
</arg>
</cmdsynopsis>
<cmdsynopsis>
<command>reboot</command>
<arg choice="opt">
-<option>hV</option>
</arg>
<arg choice="opt">
<option>--molly-guard-do-nothing</option>
</arg>
<arg choice="opt">
-- <replaceable>script_options</replaceable>
</arg>
</cmdsynopsis>
<cmdsynopsis>
<command>poweroff</command>
<arg choice="opt">
-<option>hV</option>
</arg>
<arg choice="opt">
<option>--molly-guard-do-nothing</option>
</arg>
<arg choice="opt">
-- <replaceable>script_options</replaceable>
</arg>
</cmdsynopsis>
<cmdsynopsis>
<command>coldreboot</command>
<arg choice="opt">
-<option>hV</option>
</arg>
<arg choice="opt">
<option>--molly-guard-do-nothing</option>
</arg>
<arg choice="opt">
-- <replaceable>script_options</replaceable>
</arg>
</cmdsynopsis>
<cmdsynopsis>
<command>pm-hibernate</command>
<arg choice="opt">
-<option>hV</option>
</arg>
<arg choice="opt">
<option>--molly-guard-do-nothing</option>
</arg>
<arg choice="opt">
-- <replaceable>script_options</replaceable>
</arg>
</cmdsynopsis>
<cmdsynopsis>
<command>pm-suspend</command>
<arg choice="opt">
-<option>hV</option>
</arg>
<arg choice="opt">
<option>--molly-guard-do-nothing</option>
</arg>
<arg choice="opt">
-- <replaceable>script_options</replaceable>
</arg>
</cmdsynopsis>
<cmdsynopsis>
<command>pm-suspend-hybrid</command>
<arg choice="opt">
-<option>hV</option>
</arg>
<arg choice="opt">
<option>--molly-guard-do-nothing</option>
</arg>
<arg choice="opt">
-- <replaceable>script_options</replaceable>
</arg>
</cmdsynopsis>
</refsynopsisdiv>
<refsect1>
<title>DESCRIPTION</title>
<para> &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: <command>coldreboot</command>,
<command>halt</command>, <command>reboot</command>,
<command>shutdown</command>, <command>poweroff</command>,
<command>pm-hibernate</command>,<command>pm-suspend</command>
and <command>pm-suspend-hybrid</command>.</para>
<para> Before &dhcommand; invokes the real command, all scripts in
<filename>/etc/molly-guard/run.d/</filename> have to run and exit
successfully; else, it aborts the command.
<command>run-parts(1)</command> is used to process the directory.</para>
<para> &dhcommand; passes any <replaceable>script_options</replaceable> to the
scripts, and also populates the environment with the following
variables:</para>
<itemizedlist>
<listitem><para><envar>MOLLYGUARD_CMD</envar> - the actual command
invoked by the user.</para></listitem>
<listitem><para><envar>MOLLYGUARD_DO_NOTHING</envar> - set to
<option>1</option> if this is a demo-run.</para></listitem>
<listitem><para><envar>MOLLYGUARD_SETTINGS</envar> - the path to
a shell script snippet which scripts can source to obtain
settings.</para></listitem>
</itemizedlist>
<para> &dhcommand; prints the contents of
<filename>/etc/molly-guard/messages.d/COMMAND</filename> or
<filename>/etc/molly-guard/messages.d/default</filename> to the console,
if either exists. This is due to
<filename>/etc/molly-guard/run.d/10-print-message</filename>.</para>
</refsect1>
<refsect1>
<title>GUARDING SSH SESSIONS</title>
<para> &dhcommand; was primarily designed to shield SSH connections. This
functionality (which should arguably be provided by the
<package>openssh-server</package> package) is implemented in
<filename>/etc/molly-guard/run.d/30-query-hostname</filename>.</para>
<para> This script first tests whether the command is being executed from
a <filename>tty</filename> which has been created by
<command>sshd</command>. It also checks whether the variable
<envar>SSH_CONNECTION</envar> 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.</para>
<para> You can pass the <option>--pretend-ssh</option> script option to
&dhcommand; to pretend that those tests succeeds. Alternatively, setting
<envar>ALWAYS_QUERY_HOSTNAME</envar> in
<filename>/etc/molly-guard/rc</filename> causes the script to
always query.</para>
<para> The following situations are still UNGUARDED. If you can think of
ways to protect against those, please let me know!</para>
<itemizedlist>
<listitem><para>running <application>sudo</application> within
<application>screen</application> or <application>screen</application> within
<application>sudo</application>; <application>sudo</application> eats the
<envar>SSH_CONNECTION</envar> variable, and
<application>screen</application> creates a new
<filename>pty</filename>.</para></listitem>
<listitem><para>executing those command in a remote terminal window,
that is a <application>XTerm</application> started on a remote
machine but displaying on the local <application>X</application>
server.</para></listitem>
</itemizedlist>
<para> You have been warned. You can use the
<option>--molly-guard-do-nothing</option> switch to prevent anything
from happening, e.g. <userinput>halt
--molly-guard-do-nothing</userinput>. </para>
</refsect1>
<refsect1>
<title>OPTIONS</title>
<variablelist>
<varlistentry>
<term>--molly-guard-do-nothing</term>
<listitem>
<para>
Cause &dhcommand; to print the command which would be executed,
after processing all scripts, instead of executing it.
</para>
</listitem>
</varlistentry>
<varlistentry>
<term>-h</term>
<term>--help</term>
<listitem>
<para>
Display usage information.
</para>
</listitem>
</varlistentry>
<varlistentry>
<term>-V</term>
<term>--version</term>
<listitem>
<para>
Display version information.
</para>
</listitem>
</varlistentry>
</variablelist>
</refsect1>
<refsect1>
<title>SEE ALSO</title>
<para>
<citerefentry>
<refentrytitle>shutdown</refentrytitle>
<manvolnum>8</manvolnum>
</citerefentry>,
<citerefentry>
<refentrytitle>halt</refentrytitle>
<manvolnum>1</manvolnum>
</citerefentry>,
<citerefentry>
<refentrytitle>reboot</refentrytitle>
<manvolnum>8</manvolnum>
</citerefentry>,
<citerefentry>
<refentrytitle>poweroff</refentrytitle>
<manvolnum>8</manvolnum>
</citerefentry>.
<citerefentry>
<refentrytitle>coldreboot</refentrytitle>
<manvolnum>8</manvolnum>
</citerefentry>.
<citerefentry>
<refentrytitle>pm-hibernate</refentrytitle>
<manvolnum>8</manvolnum>
</citerefentry>.
<citerefentry>
<refentrytitle>pm-suspend</refentrytitle>
<manvolnum>8</manvolnum>
</citerefentry>.
<citerefentry>
<refentrytitle>pm-suspend-hybrid</refentrytitle>
<manvolnum>8</manvolnum>
</citerefentry>.
</para>
</refsect1>
<refsect1>
<title>LEGALESE</title>
<para>
&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.
</para>
<para>
This manual page was written by &dhusername; &dhemail;.
</para>
<para>
Permission is granted to copy, distribute and/or modify this document
under the terms of the Artistic License 2.0
</para>
</refsect1>
</refentry>

19
rc Normal file
View file

@ -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"

22
run.d/10-print-message Executable file
View file

@ -0,0 +1,22 @@
#!/bin/sh
#
# 10-print-message - print a (command-specific or default) message
#
# Copyright © Andrew Ruthven <andrew@etc.gen.nz>
# Copyright © martin f. krafft <madduck@madduck.net>
# 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

102
run.d/30-query-hostname Executable file
View file

@ -0,0 +1,102 @@
#!/bin/bash
#
# 30-ask-hostname - request the user to type in the hostname of the local host
#
# Copyright © 2006-2009 martin f. krafft <madduck@madduck.net>
# Copyright © 2012 Ludovico Gardenghi <lu@dovi.co>
# Copyright © 2014 Josh Triplett <josh@joshtriplett.org>
# Copyright © 2015 Francois Marier <francois@debian.org>
# Copyright © 2017 Simó Albert i Beltran <sim6@probeta.net>
# Copyright © 2023 Andrew Ruthven <andrew@etc.gen.nz>
# 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/<pid>/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

152
shutdown.in Executable file
View file

@ -0,0 +1,152 @@
#!/bin/sh
#
# shutdown -- wrapper script to guard against accidental shutdowns
#
# Copyright © martin f. krafft <madduck@madduck.net>
# 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