Permalink
Browse files

Add confine-to-rsync

  • Loading branch information...
1 parent 3e1c1e3 commit bc7967a306a3e3f816d354bb2b5fea269c9aedf3 @FooBarWidget FooBarWidget committed Apr 27, 2011
Showing with 58 additions and 0 deletions.
  1. +12 −0 README.md
  2. +46 −0 confine-to-rsync
View
@@ -146,3 +146,15 @@ In order to preserve file permissions, the `git gc` command is run as the owner
Make it run every Sunday at 0:00 AM in cron with low I/O priority:
0 0 * * sun /tools/silence-unless-failed ionice -n 7 /tools/git-gc-repos
+
+### confine-to-rsync
+
+To be used in combination with SSH for confining an account to only rsync access. Very useful for locking down automated backup users.
+
+Consider two hypothetical servers, `backup.org` and `production.org`. Once in a while backup.org runs an automated `rsync` command, copying data from production.org to its local disk. Backup.org's SSH key is installed on production.org. If someone hacks into backup.org we don't want it to be able to login to production.org or do anything else that might cause damage, so we need to make sure that backup.org can only rsync from production.org, and only for certain directories.
+
+`confine-to-rsync` is to be installed into production.org's `authorized_keys` file as execution command:
+
+ command="/tools/confine-to-rsync /directory1 /directory2",no-port-forwarding,no-X11-forwarding,no-agent-forwarding,no-pty ssh-dss AAAAB3Nza(...rest of backup.org's key here...)
+
+`confine-to-rsync` checks whether the client is trying to execute rsync, and if so, whether the rsync is only being run on either /directory1 or /directory2. If not it will abort with an error.
View
@@ -0,0 +1,46 @@
+#!/usr/bin/env ruby
+RSYNC_COMMAND_START = "rsync --server --sender "
+RSYNC_OPTIONS_FORMAT = /-[a-z0-9\.]+ /i
+
+if ARGV.empty?
+ abort "*** You must pass a list of directories that rsync is allowed to access"
+end
+
+command = ENV['SSH_ORIGINAL_COMMAND'].to_s.dup
+if !command
+ abort "*** $SSH_ORIGINAL_COMMAND not set"
+end
+
+if !command.start_with?(RSYNC_COMMAND_START)
+ abort "*** Only rsync is allowed"
+end
+command.gsub!(/^#{Regexp.escape RSYNC_COMMAND_START}/, '')
+
+if command !~ /\A#{RSYNC_OPTIONS_FORMAT}/i
+ abort "*** Invalid rsync options detected"
+end
+command.gsub!(/\A#{RSYNC_OPTIONS_FORMAT}/, '')
+
+dirs = command.split(/ +/)
+if dirs.size < 2
+ abort "*** No directories passed to rsync"
+end
+
+if dirs.first != "."
+ abort "*** Invalid rsync directory arguments"
+end
+dirs.shift
+
+allowed_dirs = []
+ARGV.each do |dir|
+ allowed_dirs << File.expand_path(dir).sub(/\/\Z/, '')
+end
+
+dirs.each do |dir|
+ dir = File.expand_path(dir).sub(/\/\Z/, '')
+ if !allowed_dirs.include?(dir)
+ abort "*** rsync is not allowed to access #{dir}"
+ end
+end
+
+exec(ENV['SSH_ORIGINAL_COMMAND'])

0 comments on commit bc7967a

Please sign in to comment.