========BackupPC OSX Tiger Client Auto-Configure script=============

The script is below, it assumes an ssh pub/priv key with rsync setup  
for backups. It was written for OSX Tiger clients using system  
installed binaries for portability. Not tested on Panther or earlier,  
YMMV.

*****TIP*****
This script must be run as root. I highly advise doing a
$ sudo -u root -s

to gain a root shell. I say this because though the script doesn't do  
anything that would make the system "broken", if it puts a syntax  
error in /etc/sudoers you must be root to manually correct it.  
Otherwise you have to boot into single user mode mount -o  
remount,rw / and then edit to correct. . . which can be a headache.  
Yeah, it happened to me and I corrected the error...but you never  
know. ;)

Usage: backuppc_config.rb [options]
Specific options:
-h, --help                       Display this help/usage message
-r, --revert                      Undo the changes made by script


What the script does:

1. Creates a non-admin backuppc user on the client machine that will  
not appear in either the login window or in the Preferences->Accounts  
pane.
2. Creates entry in /etc/sudoers which allows the backuppc user to  
sudo rsync without a password
ckkk:3. creates a backuppc_check.rb script in /usr/local/bin which  
will verify the command source and content for the ssh rsync command  
from the backuppc server and will not allow shell access. (in other  
words *only* the backuppc user from the backuppc machine can use this  
key to execute sudo rsync and nothing else)
4. Can revert all changes done to the system
5. Performs file locking when necessary to ensure no race conditions  
on system files (a la visudo)
6. Creates all necessary directories, sub-directories, and files to  
perform the above tasks with the correct permissions.
7. At each step, error checking is performed and actions verified. If  
anything glitches, the program will raise an error with an  
appropriate message.

TODO:
-) Auto-configure the backuppc server for said client. I think this  
could be done by generating a "run once" script for each client or  
(this would be more robust), create an OCAML file which would hold  
multiple client information that is then fed to a script on the  
server. The idea would be you could walk around to clients with a usb  
drive and run the config script, each time the client info would be  
added to the OCAML 'db' in a file on the disk, then just copy the  
file to the server, run once, and clients are configured

-) Create more command line options instead of prompting



> #!/usr/bin/ruby -w
> #
> # To customize the below script you must edit values for options:
> # base_network_ip
> # public_key
> # backuppc_ip
> #
> require 'fileutils'
> require 'optparse'
> require 'ostruct'
> require 'pp'
>
> # set up some defaults
> options = OpenStruct.new
> options.backuppc_ip = "192.168.1.2"
> options.public_key = %Q!ssh-dss AAAAB3NzaC1kc3MAAACBAIh/m// 
> TVrn72tSBpAv+oCGeVb9DDPAiN7xe4UH955vKThS8p9ZGatS07n1IRQ4qdu 
> +AvElHulMKXw2jS3g5PAc1USvT9z1MJ8D87wBBTBHieSO4CyYX/ 
> 9gIEaIbX69cD7lcSHpVAyrv+N87QFUEaVXaqkP/T/ 
> aZhQBDsxbCocNlAAAAFQDpwMFEARtPSALRaKKNtc6o0ZRiHwAAAIAmmeQh/vsdZutPQ 
> +lNzo25/L5GKUvwDNxzZLTX7MPeTWV8nAPyIhFj 
> +W3mGFxNcO3IeDplVU6U0og84VXlIrSuVqUVXGHeVqrXOLD7M30YSo4lAcq79c3LFR30fV 
> TRAOoVOlrLU7BG1QfRrbsubznNN+75X2Jm/Qx/nuyLRLEBvQAAAIBgPjsmXh1Pdq0e/ 
> kb8svRL0ALcsiToNvlpCHtFgc6WcIOUSVOnLb68ujzWYF6Ew3s8yG2f5UbAVF2gTPuUIJL 
> 2V5H97EotXcCtn91O7zdJMOApxoOlrKgp2LNnq31i9LR3zOgJzH0SEukU0OcHnNJsVdapF 
> 23iVatndQSKME/mNw== [EMAIL PROTECTED]
> options.base_network_ip = "192.168.1"
> options.verbose = false
> options.revert = false
> options.user = {}
> options.user['name'] = 'backuppc'
> options.admin_id = 80
> options.backup_check = '/usr/local/bin/backup_check.rb'
> options.ip = ''
>
>
> ##########BEGIN SCRIPT METHODS###############
> def create_keys2(user,backuppc_ip,public_key)
>   authorized_keys2 = %Q!from="#{backuppc_ip}",no-pty,command="/usr/ 
> local/bin/backup_check.rb" ssh-dss #{public_key}!
>   begin
>     if !File.exists?("/Users/#{user['name']}/.ssh") then
>       FileUtils.mkdir_p "/Users/#{user['name']}/.ssh", :mode => 0700
>       File.chown user['uid'], nil, "/Users/#{user['name']}/.ssh"
>     end
>   rescue
>     raise IOError, "Failed to create /Users/#{user['name']}/.ssh"
>   end
>
>   begin
>     File.open("/Users/#{user['name']}/.ssh/authorized_keys2", 'a+')  
> {|f|
>       f.puts authorized_keys2
>       File.chown user['uid'], nil, "/Users/#{user['name']}/.ssh/ 
> authorized_keys2"
>       f.chmod(0600)
>     }
>   rescue
>     raise IOError, "Failed to create /Users/#{user['name']}/.ssh/ 
> authorized_keys2"
>   end
> end
>
> def create_backup_check(admin_id,backup_check)
>   ssh_verification_script = %Q!#\!/usr/bin/ruby
> #
> command = ENV['SSH_ORIGINAL_COMMAND']
>
> if command.nil? || command \!~ /^\\/usr\\/bin\\/sudo \\/usr\\/bin\\/ 
> rsync/
>   puts "Access Denied"
> elsif
>   system(command)
> end
>   !
>   begin
>     if not File.exists?('/usr/local/bin') then
>       FileUtils.mkdir_p '/usr/local/bin', :mode => 0755
>     end
>   rescue
>     raise IOError, "Failed to create /usr/local/bin"
>   end
>
>   begin
>     File.open(backup_check,'w') {|f|
>       f.puts ssh_verification_script
>       File.chown 0, admin_id, backup_check
>       f.chmod(0755)
>     }
>   rescue
>     raise IOError, "Failed to create /usr/local/bin/backup_check.rb"
>   end
> end
>
> def edit_sudoers(user,ip)
>   begin
>     File.open('/etc/sudoers', 'a') do |f|
>       f.flock File::LOCK_EX
>       rule = "#{user['name']} #{ip}=(root) NOPASSWD: /usr/bin/rsync"
>       f.puts rule
>       puts "The following rule is now appended to your sudoer file: "
>       puts rule
>     end
>   rescue
>     raise IOError, "/etc/sudoers edit failed:"
>   end
> end
>
> def err(type)
>   case type
>   when "USERNAME"
>     if value.empty? then
>     end
>   end
> end
>
> def valid?(ans)
>    return (ans == 'n' or ans == 'y' or ans.empty?)
> end
>
> def create_user(user)
>   user['uid'] = `/usr/bin/nireport / /users name user['uid'] | grep  
> "4[0123456789][0123456789]"`
>   if user['uid'].empty? then
>     user['uid'] = 401
>   else
>     user['uid'] = user['uid'].chomp.split[1].to_i + 1
>   end
>   # Check if the generated user['uid'] is valid, if not raise an error
>   if !`/usr/bin/id #{user['uid']} 2>/dev/null`.empty? then
>     raise SystemCallError, "Generated uid is in use: please contact  
> the developer with this exception"
>   end
>
>   puts "Confirm uid #{user['uid']} [y]"
>   confirm = gets.chomp.downcase
>
>   # Prompt for user selected user['uid'] until a valid id is found
>   while !valid?(confirm)
>     puts "Invalid option. Valid options are: [y|n]"
>     puts "Confirm uid #{user['uid']} [y]"
>     confirm = gets.chomp.downcase
>   end
>
>   if confirm == 'n'
>     puts "Please enter a valid uid"
>     user['uid'] = gets.chomp.to_i
>     valid = `/usr/bin/id #{user['uid']}`.empty?
>     while !valid
>       puts "UID is already in use"
>       puts "Please enter a valid uid: "
>       user['uid'] = gets.chomp.to_i
>       valid = `/usr/bin/id #{user['uid']} 2>/dev/null`.empty?
>     end
>   end
>
>  # create local user
>   prefix = "/usr/bin/nicl . -create /users/"
>
>   if not system("#{prefix}#{user['name']}") or
>   not system("#{prefix}#{user['name']} uid #{user['uid']}") or
>   not system("#{prefix}#{user['name']} passwd \"*\"") or
>   not system("#{prefix}#{user['name']} gid 20") or
>   not system("#{prefix}#{user['name']} shell '/bin/bash'") or
>   not system("#{prefix}#{user['name']} home '/Users/#{user 
> ['name']}'") or
>   not system("#{prefix}#{user['name']} _writers_passwd #{user 
> ['name']}") or
>   not system("#{prefix}#{user['name']} realname '#{user['name']}'")
>     raise SystemCallError, "user creation failed"
>   end
>
>   FileUtils.mkdir_p "/Users/#{user['name']}", :mode => 0750
>  `/usr/bin/passwd #{user['name']}`
>   #set ownership of new home directory to new user user['uid'] and  
> staff(20)
>   File.chown user['uid'], 20, "/Users/#{user['name']}"
>
>   return user
> end
> def get_ip(base_network_ip)
>   ip = nil
>   ip = `/sbin/ifconfig`.match(/(#{base_network_ip}\.\d+)/)
>
>   if ip.nil?
>               ip = ''
>               while ip.empty?
>       puts "A valid ip could not be found. Please input your network  
> ip: "
>       ip = gets.chomp
>       # in the regex below, insert the first 3 octets of your  
> networks ip
>       # for example, if clients receive ip's of 192.168.1.xxx, the  
> regex should be:
>       # /192\.168\.1\.\d+
>       ip = '' if !ip.match(/#{base_network_ip}\.\d+/)
>       end
>   end
>
>   return ip
> end
>
> def hide_user(user)
>   command = 'defaults write /Library/Preferences/ 
> com.apple.loginwindow '
>   command = command << "HiddenUsersList -array-add #{user['name']}"
>
>   if not (system(command)) then
>     raise SystemCallError, "Failed to hide backuppc user"
>   end
> end
>
> def revert(user,backup_check)
>   # delete the created the user
>   command = "dscl . delete /users/#{user['name']}"
>   if not system(command) then
>     puts "Failed to delete the #{user['name']} user. Perhaps it was  
> never created."
>   end
>   # delete the HiddenUsersList
>   command = "defaults delete /Library/Preferences/ 
> com.apple.loginwindow HiddenUsersList"
>   system(command)
>
>   # remove entry from /etc/sudoers if it exists
>   begin
>     f = File.new('/etc/sudoers', 'r+')
>     f.flock File::LOCK_EX
>     lines = f.readlines
>
>     f = f.reopen(f.path, 'w')
>     lines.each do |line|
>       f.print line unless line.match(/#{user['name']}.*\/usr\/bin\/ 
> rsync/)
>     end
>     f.close
>   rescue
>     raise IOError, "Editing of /etc/sudoers failed"
>   ensure
>     f.close if !f.closed?
>   end
>
>   # remove the backup_check.rb script
>   if File.exists?(backup_check) then
>     File.delete(backup_check)
>   end
> end
> #########END SCRIPT METHODS########
>
>
>
> ########BEGIN SCRIPT###############
>
> opts = OptionParser.new do |opts|
>   opts.banner = "Usage: backuppc_config.rb [options]"
>
>   opts.separator "Specific options:"
>
>   opts.on("-h", "--help", "Display this help/usage message") do
>     puts opts
>     exit
>   end
>
>   opts.on("-r", "--revert", "Undo the changes made by script") do
>     revert(options.user,options.backup_check)
>     exit
>   end
>
> end.parse!
>
> options.ip = get_ip(options.base_network_ip)
> puts "Creating backuppc user. . ."
>
> begin
>   create_user(options.user)
>   edit_sudoers(options.user, options.ip)
>   create_keys2(options.user,options.backuppc_ip,public_key)
>   create_backup_check(options.admin_id,options.backup_check)
>   hide_user(options.user)
> rescue
>   puts "Error: " + $!
> end
>

-------------------------------------------------------------------------
This SF.net email is sponsored by: Microsoft
Defy all challenges. Microsoft(R) Visual Studio 2005.
http://clk.atdmt.com/MRT/go/vse0120000070mrt/direct/01/
_______________________________________________
BackupPC-users mailing list
[email protected]
https://lists.sourceforge.net/lists/listinfo/backuppc-users
http://backuppc.sourceforge.net/

Reply via email to