Enterprise-Onion-Toolkit/eotk

481 wiersze
12 KiB
Bash
Executable File

#!/bin/sh
# Enterprise Onion Toolkit
#rsync_flags="-n" # testing
# expected by tools and libraries
cd `dirname $0` || exit 1
export EOTK_HOME=`pwd`
# for invoking myself
prog=`basename $0`
self=$EOTK_HOME/$prog
# meta
version=1.1alpha
# set project directory; this path is hard-replicated elsewhere
project_dir=$EOTK_HOME/projects.d
# mirror directory
mirrors_dir=$EOTK_HOME/mirrors.d
# onionbalance directory
ob_dir=$EOTK_HOME/onionbalance.d
ob_conf=$ob_dir/config.yaml
ob_status_sock=$ob_dir/ob-status.sock
ob_tor_conf=$ob_dir/tor.conf
ob_tor_control_sock=$ob_dir/tor-control.sock
# set path
export PATH=$EOTK_HOME/opt.d:$EOTK_HOME/lib.d:$PATH
##################################################################
# argument stripping
flag_remote=false
while : ; do
case "x$1" in
x--remote)
flag_remote=true
shift
;;
x--debug)
set -x
shift
;;
*)
break
;;
esac
done
# what are the hostnames of the remote workers?
host_list=$EOTK_HOME/eotk-hosts.conf
CloudHosts() {
# use --remote hack to forcibly stop risk of recursion...
if $flag_remote ; then
echo "localhost" # this will be treated as a magic sentinel
return 0
fi
# todo: if eotk-hosts.conf exists,
# cat it (strip comments?)
# and return
if [ -s $host_list ] ; then
cat $host_list
return 0
fi
# else we are working just on this machine
echo "localhost" # this will be treated as a magic sentinel
}
cloud_hosts=`CloudHosts` # saves multiple invocations
need_to_run_locally=false
for host in $cloud_hosts ; do
test "x$host" = "xlocalhost" && need_to_run_locally=true
done
##################################################################
# print a formatted message to stdout
Print() {
echo "$prog:" "$@"
}
# print a formatted message to stderr
Warn() {
Print "$@" 1>&2
}
# essentially the projects.d folder is a little database
ListProjects() {
(
test -d $project_dir || exit 1
cd $project_dir || exit 1
for d in *.d/ ; do
echo `basename $d .d`
done
)
}
# TODO(alecm) resolve potential clashes between project names and
# various other directory names ending in ".d"; maybe use ".proj"?
# push eotk directory to remote
DestructivePush() {
for host in $cloud_hosts ; do
test "x$host" = "xlocalhost" && continue # skip self
echo :::: push $host ::::
rsync $rsync_flags \
-av \
--delete \
--delete-excluded \
--exclude="*.log" \
--exclude="*.pid" \
--exclude="*.sock" \
--exclude="*.yaml" \
--exclude="*~" \
--exclude="docs.d/" \
--exclude="mirrors.d/" \
--exclude="onionbalance.d/" \
--exclude="secrets.d/" \
--exclude=".git/" \
--exclude=".gitignore" \
--exclude="cached-certs" \
--exclude="cached-microdesc*" \
--exclude="configure*.log" \
--exclude="hostname" \
--exclude="lock" \
--exclude="onion_service_non_anonymous" \
--exclude="private_key" \
--exclude="state" \
${EOTK_HOME}/ \
$host:${EOTK_HOME}/
done
}
# mirror remote back for log review, backup, etc
Mirror() {
for host in $cloud_hosts ; do
test "x$host" = "xlocalhost" && continue # skip self
echo :::: mirror $host ::::
test -d $mirrors_dir || mkdir -p $mirrors_dir || exit 1
rsync $rsync_flags -av \
--delete \
--delete-excluded \
--exclude="cached-certs" \
--exclude="cached-microdesc*" \
$host:${EOTK_HOME}/ $mirrors_dir/$host/
done
}
# run a command in the context of the local projects directory
RunLocally() {
action=$1
shift
project=$1
shift
echo :::: $action $project $@ ::::
sh "$project_dir/$project.d/$action.sh" "$@"
}
# $1=action, rest = project names
RunLocallyOverProjects() {
action=$1
shift # remaining arguments are projects
if [ "x$1" = "x" ] ; then # test for no args
Print error: missing project name, try: $prog projects for a list, or -a for all
return 1
elif [ "x$1" = "x-a" ] ; then # test for / expand the "-a" flag
projects=`ListProjects`
else # do what we are told
projects="$*"
fi
# loop
for project in $projects ; do
RunLocally $action $project
done
}
# run a command on remote machines, or possibly locally
InvokeRemotely() {
for host in $cloud_hosts ; do
test "x$host" = "xlocalhost" && continue # skip self
echo :::: invoking $host $* ::::
ssh "$host" "$self --remote $*"
done
}
# implement kludge to insert freshly-generated onions into a "*.tconf"
# template-configuration, so people don't have to type so much...
Populate() {
if [ -t 0 ] ; then # if stdin is a terminal, provide diags
dots=true
else
dots=false
fi
cat "$@" |
while read line ; do
case "$line" in
*%NEW_ONION%*) # legacy / shorter / more common
onion=secrets.d/`$self genkey`
echo "$line" | sed -e "s!%NEW_ONION%!$onion!"
;;
*%NEW_HARD_ONION%*) # same as NEW_ONION
onion=secrets.d/`$self genkey`
echo "$line" | sed -e "s!%NEW_HARD_ONION%!$onion!"
;;
*%NEW_SOFT_ONION%*)
onion=`$self genkey`
onion=`basename $onion .key`
echo "$line" | sed -e "s!%NEW_SOFT_ONION%!$onion!"
;;
*)
echo "$line"
;;
esac
if $dots ; then
echo ".\c" >/dev/tty
fi
done
if $dots ; then
echo "" >/dev/tty
fi
}
# get a config file and re/populate the projects directory with it
Configure() {
log=configure$$.log
for file in "$@" ; do
echo :::: configure $file ::::
case "$file" in
*.conf)
: happy bunnies
;;
*.tconf)
file2=`basename $file .tconf`.conf
if [ -s $file2 ] ; then
Print info: $file: using existing $file2
else
Print info: $file: populating $file2 with onions, please be patient...
Populate $file >$file2
fi
file="$file2"
;;
*)
Print error: bad config file suffix, was expecting: .conf, .tconf
exit 1
;;
esac
if ! $EOTK_HOME/lib.d/do-configure.pl "$file" ; then
Print error: failure processing $file: see $log
exit 1
fi
done 2>$log
Print done. logfile is $log
}
# argument 'parser' - ha!
cmd="$1" # we may need the remaining args
shift
case "$cmd" in
version)
Print $version $EOTK_HOME `uname -a`
test -d .git && git show -s --oneline
;;
projects|proj)
ListProjects
;;
populate|pop)
Populate "$@"
;;
configure|config|conf)
Configure "$@"
;;
genkey|gen)
secrets=secrets.d
test -d $secrets || mkdir -p $secrets || exit 1
(
cd $secrets
generate-onion-key.sh
)
;;
test)
$self version # recurse!
InvokeRemotely version
;;
## ACTIONS
start) # project, or "-a"
$need_to_run_locally && RunLocallyOverProjects start "$@"
InvokeRemotely start "$@"
;;
stop) # project, or "-a"
$need_to_run_locally && RunLocallyOverProjects stop "$@"
InvokeRemotely stop "$@"
;;
bounce|restart|reload) # project, or "-a"
$need_to_run_locally && RunLocallyOverProjects bounce "$@"
InvokeRemotely bounce "$@"
;;
nxreload) # project, or "-a"
$need_to_run_locally && RunLocallyOverProjects nxreload "$@"
InvokeRemotely nxreload "$@"
;;
debugon) # project, or "-a"
$need_to_run_locally && RunLocallyOverProjects debugon "$@"
InvokeRemotely debugon "$@"
;;
debugoff) # project, or "-a"
$need_to_run_locally && RunLocallyOverProjects debugoff "$@"
InvokeRemotely debugoff "$@"
;;
harvest|onions) # project, or "-a"
$need_to_run_locally && RunLocallyOverProjects harvest "$@"
InvokeRemotely harvest "$@"
;;
status) # project, or "-a"
$need_to_run_locally && RunLocallyOverProjects status "$@"
InvokeRemotely status "$@"
;;
maps|map) # project, or "-a"
$need_to_run_locally && RunLocallyOverProjects maps "$@"
InvokeRemotely maps "$@"
;;
delete) # project, or "-a"
Print $cmd not yet implemented, sorry.
;;
## DIAGS
ps)
ps auxww | egrep '\b(eotk)\b'
InvokeRemotely ps
;;
## ONIONBALANCE
push)
Print push is destructive and has been renamed, see the documentation
;;
# this used to be called 'push' but got renamed because oops.
# DO NOT USE THIS CASUALLY, LEARN FROM MY MISTAKES
remote-nuke-and-push|rnap)
$self stop -a
DestructivePush
;;
ob-config|obconfig)
if [ "x$1" = "x" ] ; then
Print error: missing project name, try: $prog projects for a list, or -a for all
exit 1
fi
# TODO: check if they are extant first, and if so, exit with error
# ob storage
test -d $ob_dir || mkdir -p $ob_dir || exit 1
# make tor conf
(
echo DataDirectory $ob_dir
echo ControlPort unix:$ob_tor_control_sock
echo PidFile $ob_dir/tor.pid
echo Log info file $ob_dir/tor.log
echo SafeLogging 1
echo HeartbeatPeriod 60 minutes
echo RunAsDaemon 1
echo "#" onionbalance
# echo SocksPort unix:$ob_dir/tor-socks.sock # curl 7.38 does not like this
echo SocksPort 127.0.0.1:9050 # meh
echo CookieAuthentication 1
echo MaxClientCircuitsPending 1024
) > $ob_tor_conf
# make ob conf
(
echo STATUS_SOCKET_LOCATION: $ob_status_sock
echo INITIAL_DELAY: 60
echo REFRESH_INTERVAL: 600
echo DESCRIPTOR_UPLOAD_PERIOD: 600
echo DESCRIPTOR_VALIDITY_PERIOD: 1800
echo PUBLISH_CHECK_INTERVAL: 900
echo LOG_LEVEL: debug
$self map "$@" | grep -v "^::::" | do-obconfig.pl
) > $ob_conf
;;
ob-start|obstart)
Print starting Tor
tor -f $ob_tor_conf >$ob_dir/tor-startup.log 2>&1
Print starting OnionBalance
onionbalance \
-s $ob_tor_control_sock \
-c $ob_conf </dev/null >$ob_dir/onionbalance.log 2>&1 & # bg
ob_pid=$!
echo $ob_pid >$ob_dir/ob.pid
;;
ob-stop|obstop)
for pidfile in $ob_dir/ob.pid $ob_dir/tor.pid ; do
test -s $pidfile || continue
pid=`cat $pidfile`
Print sending SIGTERM to $pid in $pidfile
kill $pid
done
rm -f $ob_dir/ob.pid
;;
ob-status|obstatus)
(
cd $ob_dir || exit 1
pidfiles=`find . -name "*.pid"`
if [ "x$pidfiles" != "x" ] ; then
ps -p `cat $pidfiles`
fi
echo ""
socat - unix-connect:$ob_dir/ob-status.sock
)
;;
## FREEZE/BACKUP
mirror|pull)
Mirror
;;
freeze|backup)
Mirror
(
ds=`date "+%Y%m%d%H%M%S"`
cd $mirrors_dir || exit 1
for directory in */ ; do
test -d "$directory" || exit 1 # did */ expand?
dir=`basename $directory` # strip trailing /
echo :::: freeze $dir ::::
tar cf - $dir | bzip2 > $dir-$ds.tar.bz2
done
)
;;
*)
Print usage: see README.md for documentation
exit 1
;;
esac
exit 0