Skip to content

Instantly share code, notes, and snippets.

@libryder
Created December 14, 2011 15:49
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save libryder/1477106 to your computer and use it in GitHub Desktop.
Save libryder/1477106 to your computer and use it in GitHub Desktop.
Auto SFTP mount script
#!/bin/bash
#
# Copyright (c) Finnbarr P. Murphy 2011. All rights reserved.
#
#
# ARGUMENTS: All are mandatory.
#
# l - Local folder name. This is the mount point for auto.master
# i - Local indirect name. This is the first field of auto.sshfs
# The combination of l x i provides infinite flexibility
# U - Username for SSH connection. Must be valid on Host.
# P - Password for SSH connection (plaintext). Must be valid on Host.
# r - Port for SSH connection. Must be valid SSH port on Host.
# h - Host for SSH connection. Must exist and be accessible.
# u - Local username. Must already exist.
#
#
# EXAMPLES:
#
# Assuming fpm has a uid of 1000, gid of 1000
#
# ./automount -U fpm -P abcdefg -h ultra -r 22 -u fpm -l /sshfs -i fpm
#
# adds the following line to /etc/auto.master
#
# /sshfs /etc/auto.sshfs uid=1000,gid=1000,--timeout=30,--ghost
#
# and (creates if necessary) and adds the following line (broken for clarity)
# to /etc/auto.sshfs
#
# /sshfs -fstype=fuse,port=22,rw,nodev,nonempty,noatime,allow_other, \
# max_read=65536 :sshfs\#fpm@ultra\:
#
# The following
#
# ./automount -U finnbarr -P abcbefg -h ultramax -r 22 -u fpm \
# -l /home/fpm/testmount -i finnbarr
#
# adds the following line to /etc/auto.master (broken for clarity)
#
# /home/fpm/testmount /etc/auto.home_fpm_testmount \
# uid=1000,gid=1000,--timeout=30,--ghost
#
# and (creates if necessary) and adds the following line (broken for clarity)
# to /etc/auto.home_fpm_testmount
#
# /home/fpm/testmount -fstype=fuse,port=22,rw,nodev,nonempty,noatime, \
# allow_other,max_read=65536 :sshfs\#finnbarr@ultramax\:
#
#
# NOTES:
#
# This script was written to use the bash shell. You may have to modify it if you
# wish to use another shell. It must be run as root.
#
# Remote and local users must already exist. This script does not create them.
#
# Local mount points must already exist. For safety reasons they are not
# created on the fly by default. Assign any value to CREATMOUNTDIR to
# override.
#
# If you see "Agent admitted failure to sign using key", do
# sudo gconftool-2 --set -t bool /apps/gnome-keyring/daemon-components/ssh false
#
# Do sudo apt-get update if on a new system before running this script
#
# Use fusermount -u to force umounts if necessary
#
# You have to reload your autofs maps after you modify them
#
# Indirect maps are named /etc/auto.$indirectmap (see below)
# There is a separate indrect map per $localmountpoint
#
# A partial log is provided. See automount.log
#
# ---- start configurables ----
LOG="./automount.log"
PACKAGES=( "fuse-utils" "autofs" "sshfs" "expect")
LOCALHOMEDIR="/home"
PATH=/usr/sbin:/usr/bin:/sbin:/bin
TIMEOUT=60
CREATEMOUNTDIR=y
# ----- end configurables -----
function usage {
echo "Usage: automount -U remote-username -P remote-password -h remote-host -r port"
echo " -u local-username -l local-mount-point -i local-indirect"
exit 2
}
function check_install {
PACKAGE=$1
echo -n "Checking if $PACKAGE package installed. "
RESULT=$(/usr/bin/dpkg -s $PACKAGE > /dev/null 2>&1; echo $?)
if (( RESULT == 1 )); then
echo "Not installed. Installing $PACKAGE package."
/usr/bin/apt-get -q -y install $PACKAGE >> $LOG 2>&1
RESULT=$?
if (( RESULT != 0 )); then
echo "ERROR - failed to install $PACKAGE package ($RESULT). See log"
exit 1
fi
else
echo "Already installed."
fi
}
# --- script starts here
echo
datestr=$(date)
echo "Setup started $datestr" >> $LOG
(( $EUID != 0 )) && {
echo "ERROR: You must be root to run this script."
exit 1
}
(( $# == 0 )) && usage
username=""
password=""
host=""
port=""
localmountpoint=""
localindirect=""
localuser=""
while getopts "U:P:h:r:u:l:i:" OPTION
do
case $OPTION in
U) username="$OPTARG";;
P) password="$OPTARG";;
h) host="$OPTARG";;
r) port="$OPTARG";;
l) localmountpoint="$OPTARG";;
i) localindirect="$OPTARG";;
u) localuser="$OPTARG";;
esac
done
# --- argument checking
if [[ -z "$username" ]]; then
echo "ERROR - No username entered."
exit 1
fi
if [[ -z "$password" ]]; then
echo "ERROR - No passed entered."
exit 1
fi
if [[ -z "$port" ]]; then
echo "ERROR - No port entered."
exit 1
fi
if [[ -z "$host" ]]; then
echo "ERROR - No host entered."
exit 1
fi
if [[ -z "$localmountpoint" ]]; then
echo "ERROR - No localmountpoint entered."
exit 1
fi
if [[ -z "$localindirect" ]]; then
echo "ERROR - No localindirect entered."
exit 1
fi
if [[ -z "$localuser" ]]; then
echo "ERROR - No localuser entered."
exit 1
fi
# --- do some sanity checking here
echo -n "Checking if $localuser in /etc/passwd. "
grep "^$localuser:" /etc/passwd > /dev/null 2>&1
RESULT=$?
if (( RESULT == 1 )); then
echo; echo "ERROR - $localuser not found in /etc/passwd."
exit 1
fi
echo "Yes"
echo -n "Checking connectivity with $host. "
ping -q -c 2 $host > /dev/null 2>&1
RESULT=$?
if (( RESULT == 1 )); then
echo; echo "ERROR - could not ping $host."
exit 1
fi
echo "Reachable."
echo -n "Checking local mount point. "
if [[ -d $localmountpoint ]]; then
echo "Found."
else
if [[ -z "$CREATEMOUNTDIR" ]]; then
echo "Not found."
echo "ERROR: Local mount point ($localmountpoint) not found."
echo " For safety reasons it must be manually created."
exit 1
else
mkdir -p $localmountpoint
echo "Created."
fi
fi
echo -n "Checking direct and indirect maps. "
TMPGREP=$(grep "^$localmountpoint" /etc/auto.master)
RESULT=$?
if (( RESULT == 0 )); then
INDIRECT=$(echo "$TMPGREP" | awk '{print $2}')
grep "^$localindirect" $INDIRECT > /dev/null 2>&1
RESULT=$?
if (( RESULT == 0 )); then
echo "Match."
echo "ERROR: Combination of $localmountpoint and $localindirect already in use."
exit 1
fi
fi
echo "OK."
# --- check for and install any missing packages
for PACKAGE in "${PACKAGES[@]}"
do
check_install $PACKAGE
done
# --- check fuse module status
# --- add to /etc/modprobe
# --- add $localuser to /etc/group fuse
echo -n "Checking fuse module. "
modprobe -l | grep fuse > /dev/null 2>&1
RESULT=$?
if (( RESULT == 0 )); then
echo "Loaded."
else
modprobe fuse
grep "fuse" /etc/modules > /dev/null 2>&1
RESULT=$?
if (( RESULT != 0 )); then
echo "fuse" >> /etc/modules
fi
echo "Added."
fi
echo -n "Checking if fuse group exists. "
grep "^fuse:" /etc/group > /dev/null 2>&1
RESULT=$?
if (( RESULT == 1 )); then
echo; echo "ERROR: fuse is not a member of /etc/group"
exit 1
fi
echo "Yes."
echo -n "Checking if $localuser in fuse group. "
grep "^fuse:" /etc/group | grep $localuser > /dev/null 2>&1
RESULT=$?
if (( RESULT == 1 )); then
/usr/sbin/usermod -a -G fuse $localuser
RESULT=$?
if (( RESULT == 1 )); then
echo; echo "ERROR: failed to add $localuser to fuse group"
exit 1
fi
echo "Added."
else
echo "Yes."
fi
# --- the fuse/autofs setup requires root keys to be added to
# --- to $username's authorized_keys - so generate keys here if necessary
echo -n "Checking for root ssh key files. "
SSH_KEYS_FOUND=0
if [[ -d /root/.ssh ]]; then
if [[ -s /root/.ssh/id_dsa && -s /root/.ssh/id_dsa.pub ]]; then
ssh-keygen -e -f /root/.ssh/id_dsa.pub | grep "1024-bit DSA" > /dev/null 2>&1
RESULT=$?
if (( RESULT == 0 )); then
SSH_KEYS_FOUND=1
fi
fi
fi
if (( SSH_KEYS_FOUND == 1 )); then
echo "Found."
else
echo "Not found."
rm -rf /root/.ssh
ssh-keygen -q -t dsa -N "" -f /root/.ssh/id_dsa
echo "New ssh key files (DSA protocol) generated for root."
fi
# --- add root's public key to $username@$host authorized keys
TMPROOT=expectscript-root.$$
cat <<EOT > $TMPROOT
#!/usr/bin/expect
if {[llength \$argv] != 4} {
puts "usage: \$argv0 localuser username password host"
exit 1
}
log_file -a expectscript-root.log
log_user 0
set localuser [lindex \$argv 0]
set username [lindex \$argv 1]
set password [lindex \$argv 2]
set host [lindex \$argv 3]
set timeout 60
spawn /usr/bin/ssh-copy-id -i \$localuser/.ssh/id_dsa.pub \$username@\$host
expect {
"assword: " {
send "$password\n"
expect {
"expecting." { }
"denied" { exit 1 }
timeout { exit 1 }
}
}
"(yes/no)? " {
send "yes\n"
expect {
"assword: " {
send "$password\n"
expect {
"expecting." { }
"denied" { exit 1 }
timeout { exit 1 }
}
}
}
}
}
exit 0
EOT
echo -n "Copying root's public key to $host. "
chmod 755 $TMPROOT
sleep 3
./$TMPROOT /root $username $password $host
RESULT=$?
cat $TMPROOT >> $LOG
rm -f $TMPROOT
cat expectscript-root.log >> $LOG
if (( RESULT == 0 )); then
echo "Succeeded."
rm -f expectscript-root.log
else
echo "Failed."
echo "ERROR: Check expectscript-root.log."
exit 1
fi
# --- check for $localuser public and private ssh keys
# --- we need to be $localuser here when using ssh-* utilities
echo -n "Checking for $localuser ssh key files. "
SSH_KEYS_FOUND=0
if [[ -d $LOCALHOMEDIR/$localuser ]]; then
if [[ -s $LOCALHOMEDIR/$localuser/.ssh/id_dsa && -s $LOCALHOMEDIR/$localuser/.ssh/id_dsa.pub ]]; then
sudo -u $localuser -- /usr/bin/ssh-keygen -e -f $LOCALHOMEDIR/$localuser/.ssh/id_dsa.pub | grep "1024-bit DSA" > /dev/null 2>&1
RESULT=$?
if (( RESULT == 0 )); then
SSH_KEYS_FOUND=1
fi
fi
fi
if (( SSH_KEYS_FOUND == 1 )); then
echo "Found."
else
echo "Not found."
rm -rf $LOCALHOMEDIR/$localuser/.ssh
mkdir $LOCALHOMEDIR/$localuser/.ssh
chmod 700 $LOCALHOMEDIR/$localuser/.ssh
chown -R $localuser:$localuser $LOCALHOMEDIR/$localuser/.ssh
sudo -u $localuser -- /usr/bin/ssh-keygen -q -t dsa -N "" -f $LOCALHOMEDIR/$localuser/.ssh/id_dsa
echo "New ssh key files generated (DSA protocol)"
fi
# --- add $localname's public key to $username@$host authorized keys
TMPUSER=expectscript-user.$$
cat <<EOT > $TMPUSER
#!/usr/bin/expect
if {[llength \$argv] != 4} {
puts "usage: \$argv0 localuser username password host"
exit 1
}
log_file -a expectscript-user.log
log_user 0
set localuser [lindex \$argv 0]
set username [lindex \$argv 1]
set password [lindex \$argv 2]
set host [lindex \$argv 3]
set timeout 60
spawn /usr/bin/ssh-copy-id -i \$localuser/.ssh/id_dsa.pub \$username@\$host
expect {
"assword: " {
send "$password\n"
expect {
"expecting." { }
"denied" { exit 1 }
timeout { exit 1 }
}
}
"(yes/no)? " {
send "yes\n"
expect {
"assword: " {
send "$password\n"
expect {
"expecting." { }
"denied" { exit 1 }
timeout { exit 1 }
}
}
}
}
}
exit 0
EOT
echo -n "Copying $localuser's public key to $host. "
chmod 755 $TMPUSER
sleep 3
./$TMPUSER $LOCALHOMEDIR/$localuser $username $password $host
RESULT=$?
cat $TMPUSER >> $LOG
rm -f $TMPUSER
cat expectscript-user.log >> $LOG
if (( RESULT == 0 )); then
echo "Succeeded."
rm -f expectscript-user.log
else
echo "Failed."
echo "ERROR: Check expectscript-user.log."
exit 1
fi
# --- determine the name of the indirect map associated with $localmountpoint
# --- convention is name oa local mount point stripped of leading slashs
# --- with other slashes replaced with underbar i.e. /sshfs/fpm --> auto.sshfs_fpm
indirectmap="$localmountpoint"
indirectmap=$( echo $indirectmap | tr "/" "_")
if [[ ${indirectmap:0:1} == "_" ]]; then
indirectmap=${indirectmap:1}
fi
indirectmap="/etc/auto.$indirectmap"
echo "indirectmap: $indirectmap" >> $LOG
# --- add line to /etc/auto.master
grep "$localmountpoint" /etc/auto.master > /dev/null 2>&1
RESULT=$?
if (( RESULT == 0 )); then
echo "auto.master line already exists"
else
lastline=$(sed -n '$p' /etc/auto.master)
sed -i '$d' /etc/auto.master
tuid=$(id -u $localuser)
tgid=$(id -g $localuser)
echo "$localmountpoint $indirectmap uid=$tuid,gid=$tuid,--timeout=$TIMEOUT,--ghost" >> /etc/auto.master
echo "$lastline" >> /etc/auto.master
fi
# --- add line to $indirectmap
echo "$localindirect -fstype=fuse,port=$port,rw,nodev,nonempty,noatime,allow_other,max_read=65536 :sshfs\#$username@$host\:" >> $indirectmap
# --- we are done!
datestr=$(date)
echo "Setup completed $datestr" >> $LOG
echo -e "Setup complete. Please (re)start the autofs service.\n"
exit 0
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment