#!/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> " 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 " - Full path to your ircd.conf file" echo " - 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 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 "$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...