aboutsummaryrefslogtreecommitdiff
path: root/bin
diff options
context:
space:
mode:
authorTom Ryder <tom@sanctum.geek.nz>2017-10-05 18:06:47 +1300
committerTom Ryder <tom@sanctum.geek.nz>2017-10-05 19:32:42 +1300
commit7969bce43d9c445d87a68080587298728d0d3e38 (patch)
tree6b0564dda9aecf08e3d465e4c52efb13984af419 /bin
parentAdd Module::Starter boilerplate for perlmodding (diff)
downloadMail-Run-Crypt-7969bce43d9c445d87a68080587298728d0d3e38.tar.gz
Mail-Run-Crypt-7969bce43d9c445d87a68080587298728d0d3e38.zip
Rename/separate out into app and module
This still needs a lot more work before release. In particular, I have to figure out what I'm going to do about the `passphrase` option. It's probably better to both not require it, in which case no signing is done (only encryption), and to instead allow a path to a file to be specified. The other big puzzle would be how on earth to write automated tests for it... I may end up imitating however Mail::GnuPG is testing itself.
Diffstat (limited to 'bin')
-rwxr-xr-xbin/croncrypt112
-rwxr-xr-xbin/runcrypt254
2 files changed, 254 insertions, 112 deletions
diff --git a/bin/croncrypt b/bin/croncrypt
deleted file mode 100755
index 002dd56..0000000
--- a/bin/croncrypt
+++ /dev/null
@@ -1,112 +0,0 @@
-#!/usr/bin/env perl
-
-#
-# croncrypt: Wrapper to sign and encrypt cron output and errors with PGP/MIME
-# before sending them to the default MAILTO destination.
-#
-# CRONCRYPT_KEYID=0x0A1B2C3D4E5F6G7H
-# CRONCRYPT_PASSPHRASE=hibbityboo
-# MAILTO=tom@sanctum.geek.nz
-# 0 1 * * * tom croncrypt rsync /home/tom/important-file /home/backups
-#
-# The main design goal is simplicity; just whack a «croncrypt» in front of all
-# your cron tasks, provided they don't use pipes or stderr/stdout redirects,
-# in which case you should consider putting it all into a script file anyway.
-#
-# Don't use your own GPG key for signing! Create a dedicated key just for
-# croncrypt, and sign it locally with «gpg --lsign» maybe.
-#
-# Author: Tom Ryder <tom@sanctum.geek.nz>
-# Copyright: 2014 Sanctum
-# License: Artistic 2.0 <http://opensource.org/licenses/artistic-license-2.0>
-#
-# $Id$
-#
-package Sanctum::Croncrypt;
-
-# Force me to write this properly
-use strict;
-use warnings;
-use utf8;
-use autodie;
-
-# Decree minimum Perl version required (v5.8).
-use 5.008;
-
-# Decree package version to pacify Perl::Critic
-our $VERSION = 0.1;
-
-# Pull in some required modules
-use Carp;
-use IPC::Run3;
-use Mail::GnuPG;
-use MIME::Entity;
-
-# Bail if run without arguments
-if ( !@ARGV ) {
- printf "%s\n", 'USAGE: croncrypt <command>';
- exit 1;
-}
-
-# Bail if we don't have the environment variables we need
-my @fails;
-if ( !exists $ENV{'CRONCRYPT_KEYID'} ) {
- push @fails, 'CRONCRYPT_KEYID is not set; set it to your key ID.';
-}
-if ( !exists $ENV{'CRONCRYPT_PASSPHRASE'} ) {
- push @fails,
- 'CRONCRYPT_PASSPHRASE is not set; set it to your key\'s passphrase.';
-}
-if ( !exists $ENV{'MAILTO'} ) {
- push @fails, 'MAILTO is not set; set it to the message\'s destination.';
-}
-if (@fails) {
- foreach my $fail (@fails) {
- printf {*STDERR} "croncrypt: FAIL: %s\n", $fail;
- }
- exit 1;
-}
-
-# Read details from environment
-my $recipient = $ENV{MAILTO};
-my $key = $ENV{CRONCRYPT_KEYID};
-my $passphrase = $ENV{CRONCRYPT_PASSPHRASE};
-
-# Run the command in the arguments and wait for it to finish
-my ( @output, @errors );
-run3( \@ARGV, undef, \@output, \@errors );
-
-# If there was output, mail it
-if (@output) {
- my $subject = sprintf 'croncrypt output: %s', join q{ }, @ARGV;
- mail( $subject, \@output );
-}
-
-# If there were errors, mail them
-if (@errors) {
- my $subject = sprintf 'croncrypt errors: %s', join q{ }, @ARGV;
- mail( $subject, \@errors );
-}
-
-# Send the message to the address in $ENV{MAILTO}
-sub mail {
- my ( $subject, $content ) = @_;
-
- # Build MIME object with plaintext message
- my $mime = MIME::Entity->build(
- To => $recipient,
- Subject => $subject,
- Data => $content,
- );
-
- # Encrypt the MIME object
- my $mgpg = Mail::GnuPG->new(
- key => $key,
- passphrase => $passphrase,
- );
- $mgpg->mime_signencrypt( $mime, $recipient );
-
- # Send it
- return $mime->send();
-}
-
diff --git a/bin/runcrypt b/bin/runcrypt
new file mode 100755
index 0000000..d9a24fc
--- /dev/null
+++ b/bin/runcrypt
@@ -0,0 +1,254 @@
+#!/usr/bin/env perl
+package main;
+
+# Force me to write this properly
+use strict;
+use warnings;
+use utf8;
+
+# Target Debian-Squeeze-era Perl
+# We may be able to go older; not sure yet
+use 5.010;
+
+# Import required modules
+use Carp;
+use Getopt::Long::Descriptive;
+use Mail::Run::Crypt;
+
+# Specify package version
+our $VERSION = '0.01';
+
+# Name ourselves
+our $SELF = 'runcrypt';
+
+# Read command-line options
+my ( $opt, $usage ) = describe_options(
+ "$SELF %o COMMAND [ARG1...]",
+
+ # Key ID defaults to environment RUNCRYPT_KEYID if set
+ [
+ 'keyid|k=s',
+ 'OpenPGP key ID',
+ { default => $ENV{RUNCRYPT_KEYID} // undef },
+ ],
+
+ # Key passphrase defaults to environment RUNCRYPT_PASSPHRASE if set
+ [
+ 'passphrase|p=s',
+ 'OpenPGP passphrase',
+ { default => $ENV{RUNCRYPT_PASSPHRASE} // undef },
+ ],
+
+ # MAILTO address defaults to environment MAILTO if set
+ [
+ 'mailto|m=s',
+ 'Mail destination address (MAILTO)',
+ { default => $ENV{RUNCRYPT_MAILTO} // $ENV{MAILTO} // undef },
+ ],
+
+ # Instance name (for email subjects) defaults to $SELF
+ [
+ 'name|n=s',
+ 'Instance name (included in subject lines)',
+ { default => $SELF },
+ ],
+
+ # Newline
+ [],
+
+ # Help option
+ [ 'help', 'print usage message and exit', { shortcircuit => 1 } ],
+);
+
+# Print help if requested
+if ( $opt->help ) {
+ print $usage->text
+ or carp 'Failed stdout write';
+ exit 0;
+}
+
+# Bail if run without arguments
+if ( !@ARGV ) {
+ printf {*STDERR} $usage->text
+ or carp 'Failed stderr write';
+ exit 2;
+}
+
+# Create an MCC object
+my $mrc = Mail::Run::Crypt->new(
+ keyid => $opt->keyid,
+ passphrase => $opt->passphrase,
+ mailto => $opt->mailto,
+ name => $opt->name,
+);
+
+# Run the command given in the arguments, exiting appropriately
+$mrc->run(@ARGV);
+exit $mrc->bail;
+
+__END__
+
+=pod
+
+=encoding utf8
+
+=for stopwords
+runcrypt decrypt stdout stderr GPG OpenPGP tradename licensable MERCHANTABILITY
+
+=head1 NAME
+
+runcrypt - Encrypt and mail output from command in arguments
+
+=head1 USAGE
+
+ runcrypt
+ [--keyid KEYID]
+ [--passphrase PASSPHRASE]
+ [--mailto MAILTO]
+ [--name NAME]
+ COMMAND [ARG1 ...]
+
+=head1 DESCRIPTION
+
+This program applies C<Mail::Run::Crypt> to run a command and send any output
+or error content to the specified address. More information is available in the
+documentation for that module.
+
+=head1 REQUIRED ARGUMENTS
+
+The arguments beyond the options are used as the command name to run:
+
+ runcrypt rsync -a /mnt/a remote:mnt/b
+
+=head1 OPTIONS
+
+=over 4
+
+=item C<--keyid>
+
+The GnuPG key ID that should be used to sign and encrypt the messages. This
+defaults to the value of the environment variable C<RUNCRYPT_KEYID>.
+
+=item C<--passphrase>
+
+The passphrase use to decrypt the key. This defaults to the value of the
+environment variable C<RUNCRYPT_PASSPHRASE>.
+
+=item C<--mailto>
+
+The recipient address for the encryption portion of the email. This defaults to
+the value of the environment variable C<RUNCRYPT_MAILTO> if that is set, or
+C<MAILTO> failing that, to make it suitable for use in a C<crontab(5)> file.
+
+=item C<--name>
+
+The name for this instance of the module, which will be used as the first word
+of the subject line of any email messages it sends. This defaults to
+C<runcrypt>, which is probably good enough in most cases.
+
+=head1 DIAGNOSTICS
+
+=over 4
+
+=item Failed stdout write
+
+Usage information could not be written to the standard output stream.
+
+=item Failed stderr write
+
+Usage information could not be written to the standard error stream.
+
+=head1 EXIT STATUS
+
+The program exits with the same exit value of the command that it ran, or 127
+if the command could not be run at all. See the C<bail()> method in
+C<Mail::Run::Crypt>.
+
+=head1 CONFIGURATION
+
+You will need to have a functioning GnuPG public key setup for this to work,
+including the secret key. You should definitely not use your personal key;
+generate one specifically for mail signing and encryption instead.
+
+=head1 DEPENDENCIES
+
+=over 4
+
+=item *
+
+Perl 5.10 or newer
+
+=item *
+
+C<Carp>
+
+=item *
+
+C<Getopt::Long::Descriptive>
+
+=item *
+
+C<Mail::Run::Crypt>
+
+=back
+
+=head1 INCOMPATIBILITIES
+
+This module uses C<Mail::GnuPG> and other GPG-specific code, so it won't work
+with any other OpenPGP implementations.
+
+=head1 BUGS AND LIMITATIONS
+
+Providing the C<passphrase> directly from an environment variable is not great.
+The documentation needs to make that clear and offer a less bad alternative,
+probably specifying the path to a secure file that it refuses to use unless it
+has sufficiently restrictive permissions.
+
+This is very hard to test and the author has not yet figured out how to do
+that.
+
+=head1 AUTHOR
+
+Tom Ryder C<< <tom@sanctum.geek.nz> >>
+
+=head1 LICENSE AND COPYRIGHT
+
+Copyright (C) 2017 Tom Ryder
+
+This program is free software; you can redistribute it and/or modify it under
+the terms of the Artistic License (2.0). You may obtain a copy of the full
+license at:
+
+L<http://www.perlfoundation.org/artistic_license_2_0>
+
+Any use, modification, and distribution of the Standard or Modified Versions is
+governed by this Artistic License. By using, modifying or distributing the
+Package, you accept this license. Do not use, modify, or distribute the
+Package, if you do not accept this license.
+
+If your Modified Version has been derived from a Modified Version made by
+someone other than you, you are nevertheless required to ensure that your
+Modified Version complies with the requirements of this license.
+
+This license does not grant you the right to use any trademark, service mark,
+tradename, or logo of the Copyright Holder.
+
+This license includes the non-exclusive, worldwide, free-of-charge patent
+license to make, have made, use, offer to sell, sell, import and otherwise
+transfer the Package with respect to any patent claims licensable by the
+Copyright Holder that are necessarily infringed by the Package. If you
+institute patent litigation (including a cross-claim or counterclaim) against
+any party alleging that the Package constitutes direct or contributory patent
+infringement, then this Artistic License to you shall terminate on the date
+that such litigation is filed.
+
+Disclaimer of Warranty: THE PACKAGE IS PROVIDED BY THE COPYRIGHT HOLDER AND
+CONTRIBUTORS "AS IS' AND WITHOUT ANY EXPRESS OR IMPLIED WARRANTIES. THE IMPLIED
+WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, OR
+NON-INFRINGEMENT ARE DISCLAIMED TO THE EXTENT PERMITTED BY YOUR LOCAL LAW.
+UNLESS REQUIRED BY LAW, NO COPYRIGHT HOLDER OR CONTRIBUTOR WILL BE LIABLE FOR
+ANY DIRECT, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL DAMAGES ARISING IN ANY WAY
+OUT OF THE USE OF THE PACKAGE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
+DAMAGE.
+
+=cut