#!/bin/sh # Copyright (C) 2011 Dejan Muhamedagic # # This program is free software; you can redistribute it and/or # modify it under the terms of the GNU General Public # License as published by the Free Software Foundation; either # version 2.1 of the License, or (at your option) any later version. # # This software is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU # General Public License for more details. # # You should have received a copy of the GNU General Public # License along with this library; if not, write to the Free Software # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA # # WARNING: # # The CIB secrets interface and implementation is still being # discussed, it may change # # cibsecret: manage the secrets directory /var/lib/heartbeat/lrm/secrets # # secrets are ascii files, holding just one value per file: # /var/lib/heartbeat/lrm/secrets// # # NB: this program depends on utillib.sh # . @OCF_ROOT_DIR@/lib/heartbeat/ocf-shellfuncs HA_NOARCHBIN=@datadir@/@PACKAGE_NAME@ . $HA_NOARCHBIN/utillib.sh LRM_CIBSECRETS=$HA_VARLIB/lrm/secrets PROG=`basename $0` SSH_OPTS="-o StrictHostKeyChecking=no" usage() { cat< -C: don't read/write the CIB command: set | delete | stash | unstash | get | check | sync set get check stash (if not -C) unstash (if not -C) delete sync stash/unstash: move the parameter from/to the CIB (if you already have the parameter set in the CIB). set/delete: add/remove a parameter from the local file. get: display the parameter from the local file. check: verify MD5 hash of the parameter from the local file and the CIB. sync: copy $LRM_CIBSECRETS to other nodes. Examples: $PROG set ipmi_node1 passwd SecreT_PASS $PROG stash ipmi_node1 passwd $PROG get ipmi_node1 passwd $PROG check ipmi_node1 passwd $PROG sync EOF exit $1 } fatal() { echo "ERROR: $*" exit 1 } warn() { echo "WARNING: $*" } info() { echo "INFO: $*" } check_env() { which md5sum >/dev/null 2>&1 || fatal "please install md5sum to run $PROG" if which pssh >/dev/null 2>&1; then rsh=pssh_fun rcp=pscp_fun elif which pdsh >/dev/null 2>&1; then rsh=pdsh_fun rcp=pdcp_fun elif which ssh >/dev/null 2>&1; then rsh=ssh_fun rcp=scp_fun else fatal "please install pssh, pdsh, or ssh to run $PROG" fi ps -ef | grep '[c]rmd' >/dev/null || fatal "pacemaker not running? $PROG needs pacemaker" } get_other_nodes() { crm_node -l | awk '{print $2}' | grep -v `uname -n` } check_down_nodes() { local n down_nodes down_nodes=`(for n; do echo $n; done) | sort | uniq -u` if [ -n "$down_nodes" ]; then if [ `echo $down_nodes | wc -w` = 1 ]; then warn "node $down_nodes is down" warn "you'll need to update it using $PROG sync later" else warn "nodes `echo $down_nodes` are down" warn "you'll need to update them using $PROG sync later" fi fi } pssh_fun() { pssh -qi -H "$nodes" -x "$SSH_OPTS" $* } pscp_fun() { pscp -q -H "$nodes" -x "-pr" -x "$SSH_OPTS" $* } pdsh_fun() { local pdsh_nodes=`echo $nodes | tr ' ' ','` export PDSH_SSH_ARGS_APPEND="$SSH_OPTS" pdsh -w $pdsh_nodes $* } pdcp_fun() { local pdsh_nodes=`echo $nodes | tr ' ' ','` export PDSH_SSH_ARGS_APPEND="$SSH_OPTS" pdcp -pr -w $pdsh_nodes $* } ssh_fun() { local h for h in $nodes; do ssh $SSH_OPTS $h $* || return done } scp_fun() { local h src="$1" dest=$2 for h in $nodes; do scp -pr -q $SSH_OPTS $src $h:$dest || return done } # TODO: this procedure should be replaced with csync2 # provided that csync2 has already been configured sync_files() { local crm_nodes=`get_other_nodes` local nodes=`get_live_nodes $crm_nodes` check_down_nodes $nodes $crm_nodes [ "$nodes" = "" ] && { info "no other nodes live" return } info "syncing $LRM_CIBSECRETS to `echo $nodes` ..." $rsh rm -rf $LRM_CIBSECRETS && $rsh mkdir -p `dirname $LRM_CIBSECRETS` && $rcp $LRM_CIBSECRETS `dirname $LRM_CIBSECRETS` } sync_one() { local f=$1 f_all="$1 $1.sign" local crm_nodes=`get_other_nodes` local nodes=`get_live_nodes $crm_nodes` check_down_nodes $nodes $crm_nodes [ "$nodes" = "" ] && { info "no other nodes live" return } info "syncing $f to `echo $nodes` ..." $rsh mkdir -p `dirname $f` && if [ -f "$f" ]; then $rcp "$f_all" `dirname $f` else $rsh rm -f $f_all fi } is_secret() { # assume that the secret is in the CIB if we cannot talk to # cib [ "$NO_CRM" ] || test "$1" = "$MAGIC" } check_cib_rsc() { local rsc=$1 output output=`$NO_CRM crm_resource -r $rsc -W >/dev/null 2>&1` || fatal "resource $rsc doesn't exist: $output" } get_cib_param() { local rsc=$1 param=$2 check_cib_rsc $rsc $NO_CRM crm_resource -r $rsc -g $param 2>/dev/null } set_cib_param() { local rsc=$1 param=$2 value=$3 check_cib_rsc $rsc $NO_CRM crm_resource -r $rsc -p $param -v "$value" 2>/dev/null } remove_cib_param() { local rsc=$1 param=$2 check_cib_rsc $rsc $NO_CRM crm_resource -r $rsc -d $param 2>/dev/null } localfiles() { local cmd=$1 local rsc=$2 param=$3 value=$4 local local_file=$LRM_CIBSECRETS/$rsc/$param case $cmd in "get") cat $local_file 2>/dev/null true ;; "getsum") cat $local_file.sign 2>/dev/null true ;; "set") local md5sum md5sum=`printf $value | md5sum` || fatal "md5sum failed to produce hash for resource $rsc parameter $param" md5sum=`echo $md5sum | awk '{print $1}'` mkdir -p `dirname $local_file` && echo $value > $local_file && echo $md5sum > $local_file.sign && sync_one $local_file ;; "remove") rm -f $local_file sync_one $local_file ;; *) # not reached, this is local interface ;; esac } get_local_param() { local rsc=$1 param=$2 localfiles get $rsc $param } set_local_param() { local rsc=$1 param=$2 value=$3 localfiles set $rsc $param $value } remove_local_param() { local rsc=$1 param=$2 localfiles remove $rsc $param } cibsecret_set() { local value=$1 if [ -z "$NO_CRM" ]; then [ "$current" -a "$current" != "$MAGIC" -a "$current" != "$value" ] && fatal "CIB value <$current> different for $rsc parameter $param; please delete it first" fi set_local_param $rsc $param $value && set_cib_param $rsc $param "$MAGIC" } cibsecret_check() { local md5sum local_md5sum is_secret "$current" || fatal "resource $rsc parameter $param not set as secret, nothing to check" local_md5sum=`localfiles getsum $rsc $param` [ "$local_md5sum" ] || fatal "no MD5 hash for resource $rsc parameter $param" md5sum=`printf "$current_local" | md5sum | awk '{print $1}'` [ "$md5sum" = "$local_md5sum" ] || fatal "MD5 hash mismatch for resource $rsc parameter $param" } cibsecret_get() { cibsecret_check echo "$current_local" } cibsecret_delete() { remove_local_param $rsc $param && remove_cib_param $rsc $param } cibsecret_stash() { [ "$NO_CRM" ] && fatal "no access to Pacemaker, stash not supported" [ "$current" = "" ] && fatal "nothing to stash for resource $rsc parameter $param" is_secret "$current" && fatal "resource $rsc parameter $param already set as secret, nothing to stash" cibsecret_set "$current" } cibsecret_unstash() { [ "$NO_CRM" ] && fatal "no access to Pacemaker, unstash not supported" [ "$current_local" = "" ] && fatal "nothing to unstash for resource $rsc parameter $param" is_secret "$current" || warn "resource $rsc parameter $param not set as secret, but we have local value so proceeding anyway" remove_local_param $rsc $param && set_cib_param $rsc $param $current_local } cibsecret_sync() { sync_files } check_env MAGIC="lrm://" umask 0077 if [ "$1" = "-C" ]; then NO_CRM=':' shift 1 fi cmd=$1 rsc=$2 param=$3 value=$4 case "$cmd" in set) [ $# -ne 4 ] && usage 1;; get) [ $# -ne 3 ] && usage 1;; check) [ $# -ne 3 ] && usage 1;; stash) [ $# -ne 3 ] && usage 1;; unstash) [ $# -ne 3 ] && usage 1;; delete) [ $# -ne 3 ] && usage 1;; sync) [ $# -ne 1 ] && usage 1;; *) usage 1; esac # we'll need these two often current=`get_cib_param $rsc $param` current_local=`get_local_param $rsc $param` cibsecret_$cmd $value