diff --git a/tools/cibsecret.in b/tools/cibsecret.in index 6a7ff0f957..eedb131fc7 100644 --- a/tools/cibsecret.in +++ b/tools/cibsecret.in @@ -1,356 +1,395 @@ #!/bin/sh # Copyright 2011-2020 the Pacemaker project contributors # # The version control history for this file may have further details. # # This source code is licensed under the GNU General Public License version 2 # or later (GPLv2+) WITHOUT ANY WARRANTY. # # cibsecret # # Manage the secrets directory (by default, /var/lib/pacemaker/lrm/secrets). # Secrets are ASCII files, holding one value per file: # // LRM_CIBSECRETS=@LRM_CIBSECRETS_DIR@ PROG=`basename $0` SSH_OPTS="-o StrictHostKeyChecking=no" usage() { - echo "cibsecret - A tool for managing cib secrets"; - echo ""; - echo "usage: $PROG [-C] "; - echo "--version Display version information, then exit"; - echo ""; - echo "-C: don't read/write the CIB" - echo "" - echo "command: set | delete | stash | unstash | get | check | sync" - echo "" - echo " set " - echo "" - echo " get " - echo "" - echo " check " - echo "" - echo " stash (if not -C)" - echo "" - echo " unstash (if not -C)" - echo "" - echo " delete " - echo "" - echo " sync" - echo "" - echo "stash/unstash: move the parameter from/to the CIB (if you already" - echo "have the parameter set in the CIB)." - echo "" - echo "set/delete: add/remove a parameter from the local file." - echo "" - echo "get: display the parameter from the local file." - echo "" - echo "check: verify MD5 hash of the parameter from the local file and the CIB." - echo "" - echo "sync: copy $LRM_CIBSECRETS to other nodes." - echo "" - echo "Examples:" - echo "" - echo " $PROG set ipmi_node1 passwd SecreT_PASS" - echo "" - echo " $PROG stash ipmi_node1 passwd" - echo "" - echo " $PROG get ipmi_node1 passwd" - echo "" - echo " $PROG check ipmi_node1 passwd" - echo "" - echo " $PROG sync" - - exit $1 + cat <] [] + +Options: + --help Show this message, then exit + --version Display version information, then exit + -C Don't read or write the CIB + +Commands and their parameters: + set + Set the value of a sensitive resource parameter. + + get + Display the locally stored value of a sensitive resource parameter. + + check + Verify that the locally stored value of a sensitive resource parameter + matches its locally stored MD5 hash. + + stash + Make a non-sensitive resource parameter that is already in the CIB + sensitive (move its value to a locally stored and protected file). + This may not be used with -C. + + unstash + Make a sensitive resource parameter that is already in the CIB + non-sensitive (move its value from the locally stored file to the CIB). + This may not be used with -C. + + delete + Remove a sensitive resource parameter value. + + sync + Copy all locally stored secrets to all other nodes. + +This command manages sensitive resource parameter values that should not be +stored directly in Pacemaker's Cluster Information Base (CIB). Such values +are handled by storing a special string directly in the CIB that tells +Pacemaker to look in a separate, protected file for the actual value. + +The secret files are not encrypted, but protected by file system permissions +such that only root can read or modify them. + +Since the secret files are stored locally, they must be synchronized across all +cluster nodes. This command handles the synchronization using (in order of +preference) pssh, pdsh, or ssh, so one of those must be installed. Before +synchronizing, this command will ping the cluster nodes to determine which are +alive, using fping if it is installed, otherwise the ping command. Installing +fping is strongly recommended for better performance. + +Known limitations: + + This command can only be run from full cluster nodes (not Pacemaker Remote + nodes). + + Changes are not atomic, so the cluster may use different values while a + change is in progress. To avoid problems, it is recommended to put the + cluster in maintenance mode when making changes with this command. + + Changes in secret values do not trigger a reload or restart of the affected + resource, since they do not change the CIB. If a response is desired before + the next cluster recheck interval, any CIB change (such as setting a node + attribute) will trigger it. + + If any node is down when changes to secrets are made, or a new node is + later added to the cluster, it may have different values when it joins the + cluster, before "$PROG sync" is run. To avoid this, it is recommended to + run the sync command (from another node) before starting Pacemaker on the + node. + +Examples: + + $PROG set ipmi_node1 passwd SecreT_PASS + + $PROG get ipmi_node1 passwd + + $PROG check ipmi_node1 passwd + + $PROG stash ipmi_node2 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 '[p]acemaker-controld' >/dev/null || fatal "pacemaker not running? $PROG needs pacemaker" } get_other_nodes() { crm_node -l | awk '{print $2}' | grep -v `uname -n` } get_live_nodes() { if [ `id -u` = 0 ] && which fping >/dev/null 2>&1; then fping -a $@ 2>/dev/null else local h for h; do ping -c 2 -q $h >/dev/null 2>&1 && echo $h; done fi } 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 rm -f $local_file.sign 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 } 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;; --help) usage 0;; --version) crm_attribute --version exit $?;; *) usage 1; esac . @OCF_ROOT_DIR@/lib/heartbeat/ocf-shellfuncs check_env # we'll need these two often current=`get_cib_param $rsc $param` current_local=`get_local_param $rsc $param` cibsecret_$cmd $value