hammer - Add ssh-remote directive
authorMatthew Dillon <dillon@apollo.backplane.com>
Tue, 11 Sep 2012 21:39:17 +0000 (14:39 -0700)
committerMatthew Dillon <dillon@apollo.backplane.com>
Tue, 11 Sep 2012 21:39:17 +0000 (14:39 -0700)
* Adds a feature that allows you to set up a command="..." prefix for a
  ssh key in your ~/.ssh/authorized_keys file that only runs the hammer
  utility and only with specific commands and filesystem paths.

  For example:

  command="/sbin/hammer ssh-remote mirror-read,mirror-write /path/" ssh-rsa...

  Currently requires a trailing '/' if you want to restrict the path to
  a subdirectory.  Multiple commands can be listed but the filesystem path
  restriction is only currently tested for mirror-read and mirror-write.

* This allows ssh to be used for mirroring without having to give shell
  access to the remote.

sbin/hammer/Makefile
sbin/hammer/cmd_mirror.c
sbin/hammer/cmd_remote.c [new file with mode: 0644]
sbin/hammer/hammer.8
sbin/hammer/hammer.c
sbin/hammer/hammer.h
sbin/hammer/hammer_util.h
sbin/hammer/misc.c

index 2a68ae5..9333108 100644 (file)
@@ -2,7 +2,7 @@ PROG=   hammer
 SRCS=  hammer.c ondisk.c blockmap.c cache.c misc.c cycle.c \
        cmd_show.c cmd_softprune.c cmd_history.c \
        cmd_blockmap.c cmd_reblock.c cmd_rebalance.c \
-       cmd_synctid.c cmd_stats.c \
+       cmd_synctid.c cmd_stats.c cmd_remote.c \
        cmd_pseudofs.c cmd_snapshot.c cmd_mirror.c cmd_status.c \
        cmd_cleanup.c cmd_info.c cmd_version.c cmd_volume.c \
        cmd_config.c cmd_recover.c cmd_dedup.c
index fd4f913..918c528 100644 (file)
@@ -108,6 +108,7 @@ hammer_cmd_mirror_read(char **av, int ac, int streaming)
        if (ac == 0 || ac > 2)
                mirror_usage(1);
        filesystem = av[0];
+       hammer_check_restrict(filesystem);
 
        pickup.signature = 0;
        pickup.type = 0;
@@ -771,6 +772,7 @@ hammer_cmd_mirror_write(char **av, int ac)
        if (ac != 1)
                mirror_usage(1);
        filesystem = av[0];
+       hammer_check_restrict(filesystem);
 
        pickup.signature = 0;
        pickup.type = 0;
diff --git a/sbin/hammer/cmd_remote.c b/sbin/hammer/cmd_remote.c
new file mode 100644 (file)
index 0000000..c9e8ab9
--- /dev/null
@@ -0,0 +1,81 @@
+/*
+ * Copyright (c) 2012 The DragonFly Project.  All rights reserved.
+ *
+ * This code is derived from software contributed to The DragonFly Project
+ * by Matthew Dillon <dillon@backplane.com>
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in
+ *    the documentation and/or other materials provided with the
+ *    distribution.
+ * 3. Neither the name of The DragonFly Project nor the names of its
+ *    contributors may be used to endorse or promote products derived
+ *    from this software without specific, prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+ * FOR A PARTICULAR PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE
+ * COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY OR CONSEQUENTIAL DAMAGES (INCLUDING,
+ * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
+ * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
+ * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include "hammer.h"
+
+#define REMOTE_MAXARGS 256
+
+/*
+ * Execute SSH_ORIGINAL_COMMAND if it matches
+ */
+void
+hammer_cmd_sshremote(const char *cmd, const char *target)
+{
+       char *env;
+       char *str;
+       const char *av[REMOTE_MAXARGS + 1];
+       int ac;
+
+       if ((env = getenv("SSH_ORIGINAL_COMMAND")) == NULL) {
+               fprintf(stderr, "SSH_ORIGINAL_COMMAND env missing\n");
+               exit(1);
+       }
+       env = strdup(env);
+       av[0] = "hammer";
+       av[1] = "-R";
+       av[2] = cmd;
+       av[3] = "-T";
+       av[4] = target;
+       ac = 5;
+
+       str = strsep(&env, " \t\r\n");
+       if (str == NULL) {
+               fprintf(stderr, "hammer-remote: null command\n");
+               exit(1);
+       }
+       if (strstr(str, "hammer") == NULL) {
+               fprintf(stderr, "hammer-remote: Command not 'hammer'\n");
+               exit(1);
+       }
+
+       while (ac < REMOTE_MAXARGS) {
+               av[ac] = strsep(&env, " \t\r\n");
+               ++ac;
+               if (av[ac - 1] == NULL)
+                       break;
+       }
+       execv("/sbin/hammer", (void *)av);
+       fprintf(stderr, "hammer-remote: execv failed\n");
+       exit(1);
+}
index 644f013..7c276b9 100644 (file)
@@ -43,6 +43,8 @@
 .Op Fl 2BFqrvXy
 .Op Fl b Ar bandwidth
 .Op Fl C Ar cachesize Ns Op Ns Cm \&: Ns Ar readahead
+.Op Fl R Ar restrictcmd
+.Op Fl T Ar restrictpath
 .Op Fl c Ar cyclefile
 .Op Fl e Ar scoreboardfile
 .Op Fl f Ar blkdevs
@@ -119,6 +121,13 @@ blocks.
 .Pp
 This option is typically only used with diagnostic commands
 as kernel-supported commands will use the kernel's buffer cache.
+.It Fl R Ar restrictcmd
+This option is used by hammer ssh-remote to restrict the command later
+on in the argument list.  Multiple commands may be specified, separated
+by a comma (all one argument).
+.It Fl T Ar restrictpath
+This option is used by hammer ssh-remote to restrict the filesystem path
+specified later on in the argument list.
 .It Fl c Ar cyclefile
 When pruning, rebalancing or reblocking you can tell the utility
 to start at the object id stored in the specified file.
@@ -397,6 +406,22 @@ option.
 .\" .It Ar blockmap
 .\" Dump the B-Tree, record, large-data, and small-data blockmaps, showing
 .\" physical block assignments and free space percentages.
+.\" ==== ssh-remote ====
+.It Cm ssh-remote Ar command Ar targetdir
+Used in a ssh authorized_keys line such as
+command="/sbin/hammer ssh-remote mirror-read /fubarmount" ... to allow
+mirror-read or mirror-write access to a particular subdirectory tree.
+This way you do not have to give shell access to the remote box.
+.Nm
+will obtain the original command line from the SSH_ORIGINAL_COMMAND
+environment variable, validate it against the restriction, and then
+re-exec hammer with the validated arguments.
+.Pp
+The remote hammer command does not allow the
+.Fl c
+or
+.Fl f
+options to be passed in.
 .\" ==== recover ====
 .It Cm recover Ar targetdir
 Recover data from a corrupted
index 6815379..f822516 100644 (file)
@@ -64,18 +64,20 @@ u_int64_t MemoryLimit = 1024LLU * 1024 * 1024;
 const char *SplitupOptStr;
 const char *CyclePath;
 const char *LinkPath;
+const char *RestrictTarget;
 
 int
 main(int ac, char **av)
 {
        char *blkdevs = NULL;
        char *ptr;
+       char *restrictcmd = NULL;
        u_int32_t status;
        int ch;
        int cacheSize = 0;
 
        while ((ch = getopt(ac, av,
-                           "b:c:de:hf:i:m:p:qrs:t:v2yBC:FS:X")) != -1) {
+                           "b:c:de:hf:i:m:p:qrs:t:v2yBC:FR:S:T:X")) != -1) {
                switch(ch) {
                case '2':
                        TwoWayPipeOpt = 1;
@@ -236,6 +238,14 @@ main(int ac, char **av)
                case 'F':
                        ForceOpt = 1;
                        break;
+               case 'R':
+                       if (restrictcmd == NULL)
+                               restrictcmd = optarg;
+                       break;
+               case 'T':
+                       if (RestrictTarget == NULL)
+                               RestrictTarget = optarg;
+                       break;
                case 'X':
                        CompressOpt = 1;
                        break;
@@ -254,6 +264,29 @@ main(int ac, char **av)
        signal(SIGALRM, sigalrm);
        signal(SIGINT, sigintr);
 
+       /*
+        * Check command restriction (used by hammer ssh-remote).  Several
+        * commands may be iterated with a comma.
+        */
+       if (restrictcmd) {
+               char *elm;
+
+               ptr = strdup(restrictcmd);
+               while ((elm = strsep(&ptr, ",")) != NULL) {
+                       if (strcmp(av[0], elm) == 0)
+                               break;
+               }
+               if (elm == NULL) {
+                       fprintf(stderr, "hammer-remote: request does not match "
+                                       "restricted command\n");
+                       exit(1);
+               }
+               free(ptr);
+       }
+
+       /*
+        * Parse commands
+        */
        if (strcmp(av[0], "synctid") == 0) {
                hammer_cmd_synctid(av + 1, ac - 1);
                exit(0);
@@ -375,6 +408,12 @@ main(int ac, char **av)
                hammer_cmd_softprune(av + 1, ac - 1, 1);
                exit(0);
        }
+       if (strcmp(av[0], "ssh-remote") == 0) {
+               if (ac != 3)
+                       usage(1);
+               hammer_cmd_sshremote(av[1], av[2]);
+               exit(0);
+       }
        if (strcmp(av[0], "snap") == 0) {
                hammer_cmd_snap(av + 1, ac - 1, 0, 1);
                exit(0);
@@ -611,6 +650,7 @@ usage(int exit_code)
                                  " [[user@]host:]<filesystem>\n"
                "hammer mirror-stream [[user@]host:]<filesystem>"
                                    " [[user@]host:]<filesystem>\n"
+               "hammer ssh-remote command filesystem\n"
                "hammer version <filesystem>\n"
                "hammer version-upgrade <filesystem> <version> [force]\n"
                "hammer volume-add <device> <filesystem>\n"
index 399f32d..31d40a5 100644 (file)
@@ -86,6 +86,7 @@ void hammer_cmd_show(hammer_tid_t node_offset, u_int32_t lo,
                int64_t obj_id, int depth,
                hammer_base_elm_t left_bound, hammer_base_elm_t right_bound);
 void hammer_cmd_show_undo(void);
+void hammer_cmd_sshremote(const char *cmd, const char *target);
 void hammer_cmd_recover(const char *target_dir);
 void hammer_cmd_checkmap(void);
 void hammer_cmd_prune(char **av, int ac);
@@ -130,3 +131,4 @@ void hammer_reset_cycle(void);
 
 int getpfs(struct hammer_ioc_pseudofs_rw *pfs, const char *path);
 void relpfs(int fd, struct hammer_ioc_pseudofs_rw *pfs);
+void hammer_check_restrict(const char *path);
index de1e7ec..68a5b08 100644 (file)
@@ -108,6 +108,7 @@ extern int64_t MemAreaSize;
 extern int64_t UndoBufferSize;
 extern int DebugOpt;
 extern const char *ScoreBoardFile;
+extern const char *RestrictTarget;
 extern int NumVolumes;
 extern int RootVolNo;
 extern struct volume_list VolList;
index a27dc1d..414c281 100644 (file)
@@ -167,3 +167,32 @@ score_printf(size_t i, size_t w, const char *ctl, ...)
                SSize = i + w;
        pwrite(SFd, ScoreBuf, SSize, 0);
 }
+
+void
+hammer_check_restrict(const char *filesystem)
+{
+       size_t rlen;
+       int atslash;
+
+       if (RestrictTarget == NULL)
+               return;
+       rlen = strlen(RestrictTarget);
+       if (strncmp(filesystem, RestrictTarget, rlen) != 0) {
+               fprintf(stderr, "hammer-remote: restricted target\n");
+               exit(1);
+       }
+       atslash = 1;
+       while (filesystem[rlen]) {
+               if (atslash &&
+                   filesystem[rlen] == '.' &&
+                   filesystem[rlen+1] == '.') {
+                       fprintf(stderr, "hammer-remote: '..' not allowed\n");
+                       exit(1);
+               }
+               if (filesystem[rlen] == '/')
+                       atslash = 1;
+               else
+                       atslash = 0;
+               ++rlen;
+       }
+}