335 lines
9.3 KiB
Bash
335 lines
9.3 KiB
Bash
#!/bin/bash
|
|
# gitysnc.sh, Copyright (c) 2015 Rubin
|
|
# based on linesync.sh (c) 2002 Arjen Wolfs
|
|
#
|
|
# The code contained is in this file is licenced under the terms
|
|
# and conditions as specified in the GNU General Public License.
|
|
#
|
|
# This program should be run from crontab, i.e something like:
|
|
# 0 0 * * * /home/irc/bin/gitsync.sh -s /home/irc/.ssh/id_rsa /home/irc/lib/ircd.conf /home/irc/lib/ircd.pid
|
|
#
|
|
|
|
usage() {
|
|
echo "Help: "
|
|
echo " $0 [-h|-i repository] <-p ircd.pem|-s id_rsa> <ircd.conf> <ircd.pid>"
|
|
echo ""
|
|
echo " -h - this help"
|
|
echo " -p ircd.pem - convert this ircd.pem certificate to an ssh key and use that instead of the default ~/.ssh/id_rsa"
|
|
echo " -s id_rsa - Full path to your ssh private key to use for git access (defaults to ~/.ssh/id_rsa)"
|
|
echo " -i repository-url - Perform initial setup, needed only once to set up. Provide git URL as argument"
|
|
echo " -c tagname - Load a certificate from this git tagged object (eg, servername-cert)"
|
|
echo " -C certfile - Write the certificate to this file (default: fullchain.pem in ircd.conf directory)"
|
|
echo " <ircd.conf> - Full path to your ircd.conf file"
|
|
echo " <ircd.pid> - Full path to your ircd.pid file"
|
|
echo ""
|
|
}
|
|
|
|
#Handle argument parsing
|
|
while getopts "hi:p:s:c:C:" opt; do
|
|
case $opt in
|
|
h)
|
|
usage
|
|
exit
|
|
;;
|
|
i)
|
|
dosetup="yes"
|
|
repository="$OPTARG"
|
|
|
|
;;
|
|
p)
|
|
ircdkey="yes"
|
|
kpath="$OPTARG"
|
|
;;
|
|
s)
|
|
skey="$OPTARG"
|
|
;;
|
|
c)
|
|
certtag="$OPTARG"
|
|
;;
|
|
C)
|
|
certfile="$OPTARG"
|
|
;;
|
|
\?)
|
|
echo "Unknown option: -$OPTARG" >&2
|
|
exit 1
|
|
;;
|
|
:)
|
|
echo "Option -$OPTARG requires an argument." >&2
|
|
exit 1
|
|
esac
|
|
done
|
|
shift $((OPTIND-1))
|
|
|
|
# This checks for the presence of an executable file in $PATH
|
|
locate_program() {
|
|
if [ ! -x "`which $1 2>&1`" ]; then
|
|
echo "You don't seem to have $1. Sorry."
|
|
exit 1
|
|
fi
|
|
}
|
|
|
|
# This checks for the presence of any file
|
|
check_file() {
|
|
if [ ! -f "$1" ]; then
|
|
echo "There doesn't appear to be a $1. Sorry."
|
|
exit 1
|
|
fi
|
|
}
|
|
|
|
# Try to find programs we will need
|
|
locate_program openssl
|
|
locate_program git
|
|
locate_program egrep
|
|
locate_program diff
|
|
locate_program chmod
|
|
locate_program readlink
|
|
|
|
# try to find GNU awk
|
|
awk_cmd=`which gawk`
|
|
if [ $? -ne 0 ]; then
|
|
awk_cmd=""
|
|
fi
|
|
|
|
if [ -z "$awk_cmd" ]; then
|
|
locate_program awk
|
|
is_gawk=`echo | awk --version | head -1 | egrep '^GNU.+$'`
|
|
if [ -z "$is_gawk" ]; then
|
|
echo "Your version of awk is not GNU awk. Sorry."
|
|
echo "Try: # sudo apt-get install gawk"
|
|
exit 1
|
|
fi
|
|
awk_cmd="awk"
|
|
fi
|
|
|
|
# Check for required command line parameters
|
|
if [ -z "$1" ] || [ -z "$2" ] ; then
|
|
echo "Error: No ircd.conf or ircd.pid specified"
|
|
usage
|
|
exit 1
|
|
fi
|
|
|
|
if [ -n "$skey" ] && [ -n "$ircdkey" ]; then
|
|
echo "Error: You cannot use -s and -p together. Pick one method to authenicate."
|
|
usage
|
|
exit;
|
|
fi
|
|
|
|
if [ -z "$skey" ] && [ -z "$ircdkey" ]; then
|
|
echo "Error: You must provide -s or -p. Pick one method to authenicate."
|
|
usage
|
|
exit;
|
|
fi
|
|
|
|
|
|
if [ -z "$skey" ]; then
|
|
skey="$HOME/.ssh/id_rsa"
|
|
fi
|
|
|
|
# check and set up stuff
|
|
diff_cmd="diff"
|
|
cpath="$1"
|
|
ppath="$2"
|
|
check_file "$cpath"
|
|
dpath=`dirname "$cpath"`
|
|
dpath=`readlink -f "$dpath"`
|
|
lpath="$dpath/linesync"
|
|
#check_file $lpath
|
|
save_dir="$PWD"; cd "$dpath"
|
|
tpath="$PWD"; cd "$save_dir"
|
|
tmp_path="$dpath/tmp"
|
|
ipath="$tmp_path/ssh.pem"
|
|
mkdir "$tmp_path" > /dev/null 2>&1
|
|
|
|
# Not all versions of date support %s, work around it
|
|
TS=`date +%Y%m%d%H%M%S`
|
|
TMPFILE="$tmp_path/linesync.$TS"
|
|
TMPCERT="$tmp_path/cert.$TS"
|
|
|
|
echo '#!/bin/bash' > "$tmp_path/git.sh"
|
|
|
|
#If they specified -p, we generate the git ssh key from ircd.pem
|
|
if [ -n "$ircdkey" ]; then
|
|
|
|
check_file "$kpath"
|
|
# first get the private key by just grabbing the lines that match...
|
|
awk '/BEGIN .*PRIVATE KEY/,/END .*PRIVATE KEY/' "$kpath" > "$ipath"
|
|
# Then we'll get the public key more properly..
|
|
if ! openssl x509 -in "$kpath" -pubkey -noout >> "$ipath"; then
|
|
echo "Error: I could not use $kpath as a key for some reason. Stopping!"
|
|
exit
|
|
fi
|
|
|
|
chmod 600 "$tmp_path/ssh.pem"
|
|
|
|
#Override git's ssh command so we can force our custom identity and no password
|
|
echo -e "ssh -oPasswordAuthentication=no -i \"$ipath\" \"\$1\" \"\$2\"\n" >> "$tmp_path/git.sh"
|
|
else
|
|
#Override git's ssh command so we can force our custom identity and no password
|
|
echo -e "ssh -oPasswordAuthentication=no -i \"$skey\" \"\$1\" \"\$2\"\n" >> "$tmp_path/git.sh"
|
|
fi
|
|
|
|
chmod a+x "$tmp_path/git.sh"
|
|
export GIT_SSH="$tmp_path/git.sh"
|
|
|
|
|
|
if [ "$dosetup" = "yes" ]; then
|
|
echo "Doing initial setup with repository $repository" >&2
|
|
|
|
# Check if destination already has a git repo
|
|
if [ -d "$lpath/.git" ]; then
|
|
echo "Doing setup.. but $lpath already contains a git repository."
|
|
echo "Remove it first if you want to re-clone: rm -rf $lpath/*"
|
|
exit 2
|
|
fi
|
|
# Check if directory exists and is not empty (allow empty dirs for bind mounts)
|
|
if [ -d "$lpath" ] && [ -n "$(ls -A "$lpath" 2>/dev/null)" ]; then
|
|
echo "Doing setup.. but destination directory $lpath already exists and is not empty."
|
|
echo "Move it out of the way and try again"
|
|
exit 2
|
|
fi
|
|
echo "Note: your public key (linesync admin will have added this to keydir):"
|
|
|
|
if [ -n "$ircdkey" ]; then
|
|
ssh-keygen -i -m PKCS8 -f "$tmp_path/ssh.pem"
|
|
#cat $ipath
|
|
else
|
|
cat "$skey".pub
|
|
fi
|
|
|
|
prevdir=`pwd`
|
|
cd "$dpath"
|
|
git clone "$repository" "$lpath"
|
|
if [ -d "$lpath"/.git ]; then
|
|
echo "Initial setup success"
|
|
exit 0
|
|
else
|
|
echo "Problem with initial setup. See above"
|
|
exit 5
|
|
fi
|
|
fi
|
|
|
|
#Check for the git repository
|
|
if [ ! -d "$lpath" ]; then
|
|
echo "Cannot find a git repository at $lpath."
|
|
echo "check ircd.conf path argument, or re-run with -i <repository> to perform initial setup"
|
|
usage
|
|
exit 6
|
|
fi
|
|
|
|
if [ ! -d "$lpath"/.git ]; then
|
|
echo "Error: $lpath is not a git repository. ?!"
|
|
usage
|
|
exit 10
|
|
fi
|
|
|
|
#update the repository from upstream
|
|
prevdir=`pwd`
|
|
cd "$lpath"
|
|
git reset -q --hard origin/master
|
|
git fetch --tags -f --quiet
|
|
git merge -q origin/master
|
|
if [ ! -z "$certtag" ]; then
|
|
#Store the cert in a temp file
|
|
git show "$certtag" > "$TMPCERT"
|
|
fi
|
|
cd "$prevdir"
|
|
|
|
#Copy the data to the temp file
|
|
cp "$lpath/linesync.data" "$TMPFILE"
|
|
|
|
if [ ! -s "$TMPFILE" ]; then
|
|
echo "Unable find retrieve $lpath/linesync.data, Sorry."
|
|
rm "$TMPFILE" > /dev/null 2>&1
|
|
exit 1
|
|
fi
|
|
|
|
# check our ircd.conf
|
|
ircd_setup=`egrep '^# (BEGIN|END) LINESYNC$' "$cpath"|wc -l`
|
|
if [ $ircd_setup != 2 ]; then
|
|
cp "$cpath" "$cpath.orig"
|
|
echo "Performing initial merge on $cpath, original file saved as $cpath.orig."
|
|
|
|
echo "# Do NOT remove the following line, linesync.sh depends on it!" >> $cpath
|
|
echo "# BEGIN LINESYNC" >> $cpath
|
|
echo "# END LINESYNC" >> $cpath
|
|
echo "# Do not remove the previous line, linesync.sh depends on it!" >> $cpath
|
|
|
|
# Do an initial merge to remove duplicates
|
|
inpath="$tmp_path/linesync.tmp.$TS"
|
|
$awk_cmd '
|
|
{
|
|
if (!loaded_template) {
|
|
command="cat " tempfile; tlines=0;
|
|
while ((command | getline avar) > 0) { template[tlines]=avar; tlines++ }
|
|
close(command)
|
|
loaded_template++
|
|
}
|
|
dup_line=0
|
|
for (i=0; i<tlines; i++) {
|
|
if (tolower($0)==tolower(template[i])) { dup_line++; break }
|
|
}
|
|
if (!dup_line) print $0
|
|
} ' "tempfile='$TMPFILE'" < "$cpath" > "$inpath"
|
|
else
|
|
inpath="$cpath"
|
|
fi
|
|
|
|
# Replace the marked block in ircd.conf with the new version
|
|
|
|
$awk_cmd '
|
|
$0=="# BEGIN LINESYNC" { chop++; print; next }
|
|
$0=="# END LINESYNC" {
|
|
command="cat " syncfile
|
|
while ((command | getline avar) > 0) { print avar }
|
|
close(command)
|
|
chop--
|
|
}
|
|
{ if (!chop) print $0 }
|
|
' "syncfile='$TMPFILE'" < "$inpath" > "$tmp_path/linesync.new.$TS"
|
|
|
|
# run a diff between current and new confs to see if we updated anything
|
|
# no point sending the ircd a -HUP if this is not needed, especially on a
|
|
# busy network, such as Undernet.
|
|
diff=`"$diff_cmd" "$cpath" "$tmp_path/linesync.new.$TS"`
|
|
if [ ! -z "$diff" ]; then
|
|
# Changes were detected
|
|
|
|
# Back up the current ircd.conf and replace it with the new one
|
|
cp "$cpath" "$dpath/ircd.conf.bk"
|
|
cp "$tmp_path/linesync.new.$TS" "$cpath"
|
|
|
|
# Rehash ircd (without caring wether or not it succeeds)
|
|
kill -HUP `cat "$ppath" 2>/dev/null` > /dev/null 2>&1
|
|
|
|
#todo: kill iauthd?
|
|
fi
|
|
|
|
if [ ! -z "$certtag" ]; then
|
|
# Use provided certfile path or default to fullchain.pem
|
|
: "${certfile:=$dpath/fullchain.pem}"
|
|
|
|
if [ ! -f "$certfile" ]; then
|
|
cdiff="yes"
|
|
else
|
|
cdiff=`"$diff_cmd" "$certfile" "$TMPCERT"`
|
|
fi
|
|
if [ ! -z "$cdiff" ]; then
|
|
#Changes detected
|
|
if [ -f "$certfile" ]; then
|
|
cp "$certfile" "${certfile}.backup"
|
|
fi
|
|
cp "$TMPCERT" "$certfile"
|
|
|
|
# Rehash pem files (without caring wether or not it succeeds)
|
|
kill -USR1 `cat "$ppath" 2>/dev/null` > /dev/null 2>&1
|
|
fi
|
|
fi
|
|
|
|
|
|
# (Try to) clean up
|
|
if [ -n "$tmp_path" ] && [ -d "$tmp_path" ]; then
|
|
rm -rf "$tmp_path" #> /dev/null 2>&1
|
|
fi
|
|
|
|
# That's it...
|