#!/bin/ksh93 # ALTRAN_PROLOG_BEGIN_TAG # This is an automatically generated prolog. # # Copyright (C) Altran ACT S.A.S. 2017,2018,2019,2020,2021. All rights reserved. # # ALTRAN_PROLOG_END_TAG # # IBM_PROLOG_BEGIN_TAG # This is an automatically generated prolog. # # 61haes_r720 src/43haes/lib/ksh93/hacmp/KLIB_HACMP_modify_cluster_security.sh 1.12 # # Licensed Materials - Property of IBM # # COPYRIGHT International Business Machines Corp. 2010 # All Rights Reserved # # US Government Users Restricted Rights - Use, duplication or # disclosure restricted by GSA ADP Schedule Contract with IBM Corp. # # IBM_PROLOG_END_TAG # @(#) 7d4c34b 43haes/lib/ksh93/hacmp/KLIB_HACMP_modify_cluster_security.sh, 726, 2147A_aha726, Feb 05 2021 09:50 PM #============================================================================ # # Name: KLIB_HACMP_modify_cluster_security # # Description: This is the main, FPATH function that is invoked by clmgr # to modify security settings in the cluster environment. # If is invoked from the "KLIB_HACMP_modify_cluster" code # when the "clmgr modify cluster" operation is invoked with # any security settings specified. # # Inputs: See the "devDoc()" function, below. # # Outputs: The properties hash is populated. The only other outputs are # any error messages that might be needed. # # Returns: Zero if no errors are detected. Otherwise, an appropriate # non-zero value is returned. Refer to the "RETURN" section # of the "devDoc()" function, below, for the standard return # code values/meanings for clmgr. # #============================================================================ function KLIB_HACMP_modify_cluster_security { . $HALIBROOT/log_entry "$0()" "$CL" : version=@(#) 7d4c34b 43haes/lib/ksh93/hacmp/KLIB_HACMP_modify_cluster_security.sh, 726, 2147A_aha726, Feb 05 2021 09:50 PM : INPUTS: $* typeset -n properties=$1 shift typeset user_input=$* typeset -u level=${1//\"/} typeset -u algorithm=${2//\"/} typeset mechanism=${3//\"/} typeset certificate=${4//\"/} typeset private_key=${5//\"/} [[ $CLMGR_LOGGING == 'med' ]] && set +x # Only trace param values #=================================== : Declare and initialize variables #=================================== typeset -i rc=$RC_UNKNOWN timeout=40 i=0 typeset -i errors=0 i=0 ret_value=0 typeset -i LVL_CHANGED=0 MECH_CHANGED=0 ALG_CHANGED=0 typeset -u mechanismUC=$mechanism typeset DEFAULT_CERTIFICATE="/etc/security/cluster/cacert.der" typeset DEFAULT_PRIVATE_KEY="/etc/security/cluster/cakey.der" #================================================================ : Check for a defined cluster. No need to continue without one. #================================================================ CL=$LINENO isClusterDefined if (( $? != RC_SUCCESS )); then dspmsg -s $CLMGR_SET $CLMGR_MSGS 35 "\nERROR: no cluster is defined yet.\n\n" 1>&2 rc=$RC_MISSING_DEPENDENCY elif [[ $user_input == *([[:space:]]) ]]; then dspmsg -s $CLMGR_SET $CLMGR_MSGS 401 '\nERROR: no valid modifications were specified for "%1$s".\n\n' "$(cllsclstr -cS | cut -d: -f2)" 1>&2 rc=$RC_MISSING_INPUT else #======================================= : Retrieve the current security settings #======================================= typeset -A clattrs CLMGR_NO_REMOTE=true CL=$LINENO KLIB_HACMP_get_cluster_attributes clattrs # Get the current level curr_level=${clattrs[LEVEL]} # If level is not provided then assign it to 2 by default. if [[ -n $level ]]; then LVL_CHANGED=1 case $level in D*) level="DISABLED" ;; L*) level="LOW" ;; M*) level="MEDIUM" ;; H*) level="HIGH" ;; esac elif [[ -z $level ]]; then level="MEDIUM" fi # If algorithm is not provided then get the algorithm from previous configuration, if it is empty then assign it to AES by default. if [[ -n $algorithm ]]; then ALG_CHANGED=1 case $algorithm in D*) algorithm="DES" ;; 3*) algorithm="3DES" ;; A*) algorithm="AES" ;; esac elif [[ -z $algorithm ]]; then algorithm=${clattrs[ALGORITHM]} fi if [[ -z $algorithm ]]; then algorithm="AES" fi # If mechanism is not provided then get the mechanism from previous configuration, if it is empty then assign it to self-sign by default. if [[ -n "$mechanism$certificate$private_key" ]]; then MECH_CHANGED=1 elif [[ -z $mechanism ]]; then mechanism=${clattrs[MECHANISM]} [[ $mechanism == "self_sign" ]] && mechanism="self-sign" fi if [[ -z $mechanism ]]; then mechanism="self-sign" fi if [[ -n $mechanism ]]; then case $mechanismUC in O*) mechanism="OpenSSL" ;; *SSL*) mechanism="OpenSSL" ;; SE*) mechanism="self-sign" ;; *SSH*) mechanism="ssh" ;; esac fi if [[ -z $certificate ]]; then if [[ $mechanism == self* && -f $DEFAULT_CERTIFICATE ]]; then certificate=$DEFAULT_CERTIFICATE fi fi if [[ -z $private_key ]]; then if [[ $mechanism == self* && -f $DEFAULT_PRIVATE_KEY ]]; then private_key=$DEFAULT_PRIVATE_KEY fi fi if [[ -z $LANG || $LANG == @(C|en|En|EN)* ]]; then # : For non-English values, convert to English for internal processing only # typeset -u STR="" typeset -i count=1 IFS=, for STR in $(dspmsg -s $CLMGR_SET $CLMGR_MSGS 438 "LOW,MEDIUM,HIGH,DISABLED") do if [[ $STR == $level* ]]; then level=$(LC_ALL=C dspmsg -s $CLMGR_SET $CLMGR_MSGS 438 "LOW,MEDIUM,HIGH,DISABLED" | cut -d, -f$count) break fi (( count++ )) done fi fi #================== : Validate inputs #================== if (( rc == RC_UNKNOWN || rc == RC_SUCCESS )) then if (( LVL_CHANGED )) && [[ $level != @(D|L|M|H)* && $level != +([[:digit:]]) ]] then dspmsg -s $CLMGR_SET $CLMGR_MSGS 110 '\nERROR: invalid value specified for "%1$s": "%2$s".\n' LEVEL "$level" 1>&2 typeset valid=$(dspmsg -s $CLMGR_SET $CLMGR_MSGS 438 "LOW,MEDIUM,HIGH,DISABLED") dspmsg -s $CLMGR_SET $CLMGR_MSGS 3 'Valid values: %1$s\n\n' "${valid//,/, } (0-3)" 1>&2 rc=$RC_INCORRECT_INPUT fi if [[ $level != @(0|D)* ]]; then if (( ALG_CHANGED )) && [[ $algorithm != @(D|3|A)* ]]; then dspmsg -s $CLMGR_SET $CLMGR_MSGS 110 '\nERROR: invalid value specified for "%1$s": "%2$s".\n' ALGORITHM "$algorithm" 1>&2 dspmsg -s $CLMGR_SET $CLMGR_MSGS 3 'Valid values: %1$s\n\n' "DES, 3DES, AES" 1>&2 rc=$RC_INCORRECT_INPUT fi if (( MECH_CHANGED )) && [[ $mechanismUC != *@(O|SE|SSL|SSH)* ]];then dspmsg -s $CLMGR_SET $CLMGR_MSGS 110 '\nERROR: invalid value specified for "%1$s": "%2$s".\n' MECHANISM "$mechanism" 1>&2 dspmsg -s $CLMGR_SET $CLMGR_MSGS 3 'Valid values: %1$s\n\n' "OpenSSL, SelfSigned, SSH" 1>&2 rc=$RC_INCORRECT_INPUT fi if [[ -n $certificate && $certificate != /* ]] then dspmsg -s $CLMGR_SET $CLMGR_MSGS 106 '\nERROR: the specified path does not appear to be in absolute format:\n%1$s\n\n' "$certificate" 1>&2 rc=$RC_INCORRECT_INPUT fi if [[ -n $certificate && ! -e $certificate ]] then cl_dspmsg -s $CLMGR_SET $CLMGR_MSGS 107 '\nERROR: the specified path/file does not appear to exist on "%2$s": %1$s\n\n' "$certificate" "$LOCAL_NODE" 1>&2 rc=$RC_NOT_FOUND fi if [[ -n $private_key && $private_key != /* ]] then dspmsg -s $CLMGR_SET $CLMGR_MSGS 106 '\nERROR: the specified path does not appear to be in absolute format:\n%1$s\n\n' "$private_key" 1>&2 rc=$RC_INCORRECT_INPUT fi if [[ -n $private_key && ! -e $private_key ]] then cl_dspmsg -s $CLMGR_SET $CLMGR_MSGS 107 '\nERROR: the specified path/file does not appear to exist on "%2$s": %1$s\n\n' "$private_key" "$LOCAL_NODE" 1>&2 rc=$RC_NOT_FOUND fi fi fi #======================================================================= : Customers are allowed to do a few invalid things... as long as they : are disabling security anyway. If they are *not* disabling security, : though, then additional validations must be performed and must pass. #======================================================================= if (( $rc == RC_UNKNOWN || $rc == RC_SUCCESS)) && [[ $level != @(0|D)* ]]; then #====================================================================== : If using Self-Signed, either both the certificate *and* the private : key need to be specified, or neither, unless the one specified : happens to be the default anyway. If neither is specified, both : files will be automatically generated, but only for Self-Signed. #====================================================================== if [[ $mechanism == self* ]]; then if [[ $certificate == $DEFAULT_CERTIFICATE && \ $private_key != $DEFAULT_PRIVATE_KEY ]] || \ [[ $certificate != $DEFAULT_CERTIFICATE && \ $private_key == $DEFAULT_PRIVATE_KEY ]] then cl_dspmsg -s $CLMGR_SET $CLMGR_MSGS 655 "\nERROR: the private key and certificate files must match. Either use both default files, or neither.\n\n" 1>&2 rc=$RC_INCORRECT_INPUT fi #====================================================================== : If not using Self-Signed, then both a certificate and a private key : must always be specified. Defaults are only provided for Self- : Signed. #====================================================================== elif [[ -n $mechanism ]]; then if [[ -z $private_key || $private_key == "" || \ $private_key == $DEFAULT_PRIVATE_KEY || \ -z $certificate || $certificate == "" || \ $certificate == $DEFAULT_CERTIFICATE ]] then CL=$LINENO cl_dspmsg -s $CLMGR_SET $CLMGR_MSGS 656 "\nERROR: a matching private key and certificate combination must be provided for security mechanism \"%1\$s\".\n\n" "$mechanism" 1>&2 rc=$RC_INCORRECT_INPUT fi fi fi # If level is given as disable and trying to modify other paramters then update input as incorrect if (( $rc == RC_UNKNOWN || $rc == RC_SUCCESS )) && [[ $level == @(0|D)* ]]; then if (( ( ALG_CHANGED + MECH_CHANGED ) > 0 )); then cl_dspmsg -s $CLMGR_SET $CLMGR_MSGS 426 '\nERROR: cluster security is being disabled, rendering the specified configuration options invalid: %1$s\n\n' "$_NAME_VALUE_PAIRS" 1>&2 rc=$RC_INCORRECT_INPUT fi fi #==================================================================== : Modify the cluster security if no input errors have been detected #==================================================================== if (( $rc == RC_UNKNOWN || $rc == RC_SUCCESS )); then if [[ $level == @(0|D)* ]]; then if [[ $curr_level != @(0|D)* ]]; then dspmsg -s $CLMGR_SET $CLMGR_MSGS 657 "Warning: disabling security for all cluster communication." print ".. " for (( i=5; i>0; i-- )); do print -n "$i... " sleep 1 done print else dspmsg -s $CLMGR_SET $CLMGR_MSGS 1434 "Warning: Cluster security is already disabled.\n" log_return_msg "$RC_SUCCESS" "$0()" "$LINENO" return $? fi fi typeset lOpt= cOpt= fOpt= tOpt= sOpt= if [[ -n $level ]]; then if [[ $level == @(0|D)* ]]; then level=0 # Level is declared as integer in odm lOpt=" -l 0" else # As there is no functionality difference among LOW, MEDIUM and HIGH levels we are updating # level as 2 by default to match with the latest caa security smit screens level=2 lOpt=" -l 2" fi fi [[ -n $algorithm ]] && sOpt=" -s $algorithm" [[ -n $mechanism ]] && tOpt=" -t $mechanism" [[ -n $certificate ]] && cOpt=" -c $certificate" [[ -n $private_key ]] && fOpt=" -f $private_key" #==================================== : Change the security configuration #==================================== if (( LVL_CHANGED )) && [[ $level == @(0|D)* ]]; then #=================== : Disabling security #=================== print "$0()[$LINENO]($SECONDS): savesecconf$lOpt" >>$CLMGR_TMPLOG eval savesecconf$lOpt ret_value=$? (( $ret_value != RC_SUCCESS )) && rc=$RC_ERROR || rc=$RC_SUCCESS print "$0()[$LINENO]($SECONDS): savesecconf$lOpt RC: $ret_value" >>$CLMGR_TMPLOG elif (( MECH_CHANGED )); then print "$0()[$LINENO]($SECONDS): savesecconf$lOpt$sOpt$tOpt$cOpt$fOpt" >>$CLMGR_TMPLOG eval savesecconf$lOpt$sOpt$tOpt$cOpt$fOpt ret_value=$? (( $ret_value != RC_SUCCESS )) && rc=$RC_ERROR || rc=$RC_SUCCESS print "$0()[$LINENO]($SECONDS): savesecconf$lOpt$sOpt$tOpt$cOpt$fOpt RC: $ret_value" >>$CLMGR_TMPLOG elif (( ALG_CHANGED )); then print "$0()[$LINENO]($SECONDS): savesecconf$sOpt" >>$CLMGR_TMPLOG eval savesecconf$sOpt ret_value=$? (( $ret_value != RC_SUCCESS )) && rc=$RC_ERROR || rc=$RC_SUCCESS print "$0()[$LINENO]($SECONDS): savesecconf$sOpt RC: $ret_value" >>$CLMGR_TMPLOG elif (( LVL_CHANGED )); then print "$0()[$LINENO]($SECONDS): savesecconf$lOpt" >>$CLMGR_TMPLOG eval savesecconf$lOpt ret_value=$? (( $ret_value != RC_SUCCESS )) && rc=$RC_ERROR || rc=$RC_SUCCESS print "$0()[$LINENO]($SECONDS): savesecconf$lOpt RC: $ret_value" >>$CLMGR_TMPLOG fi #====================== : Check communication #====================== dspmsg -s $CLMGR_SET $CLMGR_MSGS 659 "\nTesting cluster communication using the new security configuration...\n" 1>&2 set_alarm "$timeout" $! KILL "$(dspmsg -s $CLMGR_SET $CLMGR_MSGS 660 'Warning: could not verify cluster communication in the allotted time ($timeout seconds).\n' \"$timeout\")" 1>&2 for (( i=0; i<=6; i+=1 )); do sleep 3 print "$0()[$LINENO]($SECONDS): clcmd /bin/hostname" >>$CLMGR_TMPLOG # Always log commands clcmd /bin/hostname >$TMPDIR/clmgr.KHmc.sec.$$ 2>&1 typeset -i commrc=$? print "$0()[$LINENO]($SECONDS): clcmd RC: $commrc" >>$CLMGR_TMPLOG # Always log command result if (( $commrc != RC_SUCCESS && i >= 6 )); then cl_dspmsg -s $CLMGR_SET $CLMGR_MSGS 661 "\nERROR: communication within the cluster appears to be compromised. A manual restart of clcomd on each affected node may be needed.\n" 1>&2 cat $TMPDIR/clmgr.KHmc.sec.$$ 1>&2 rc=$RC_ERROR elif (( $commrc == RC_SUCCESS )); then cl_dspmsg -s $CLMGR_SET $CLMGR_MSGS 662 "Cluster communication using the new security configuration appears to be functioning properly.\n" 1>&2 break fi rm -f $TMPDIR/clmgr.KHmc.sec.$$ done clear_alarm fi #========================= : Update HACMPsecurity ODM #========================= if (( $rc == RC_UNKNOWN || $rc == RC_SUCCESS )); then typeset sec_odm_entries="" sec_odm_entries=$(clodmget HACMPsecurity) if [[ -z $sec_odm_entries ]]; then if [[ $level == @(0|D)* ]]; then printf "%s\n\t%s\n\t%s\n\t%s\n\t%s\n\t%s\n" "HACMPsecurity:" "level=$level" "algorithm=\"\"" "mechanism=\"\"" "certificate_file=\"\"" "private_keyfile=\"\"" | odmadd (( $? != RC_SUCCESS )) && rc=$RC_ERROR || rc=$RC_SUCCESS else # Default files will not be generated on home node but they will generated on remote nodes, hence we should update our odm with default values. certificate=$DEFAULT_CERTIFICATE private_key=$DEFAULT_PRIVATE_KEY printf "%s\n\t%s\n\t%s\n\t%s\n\t%s\n\t%s\n" "HACMPsecurity:" "level=$level" "algorithm=$algorithm" "mechanism=$mechanism" "certificate_file=$certificate" "private_keyfile=$private_key" | odmadd (( $? != RC_SUCCESS )) && rc=$RC_ERROR || rc=$RC_SUCCESS fi elif [[ -n $sec_odm_entries ]]; then if [[ $level == @(0|D)* ]]; then printf "%s\n\t%s\n\t%s\n\t%s\n\t%s\n\t%s\n" "HACMPsecurity:" "level=$level" "algorithm=\"\"" "mechanism=\"\"" "certificate_file=\"\"" "private_keyfile=\"\"" | odmchange -o HACMPsecurity (( $? != RC_SUCCESS )) && rc=$RC_ERROR || rc=$RC_SUCCESS else printf "%s\n\t%s\n\t%s\n\t%s\n\t%s\n\t%s\n" "HACMPsecurity:" "level=$level" "algorithm=$algorithm" "mechanism=$mechanism" "certificate_file=$certificate" "private_keyfile=$private_key" | odmchange -o HACMPsecurity (( $? != RC_SUCCESS )) && rc=$RC_ERROR || rc=$RC_SUCCESS fi fi if (( rc == RC_ERROR )); then print "$0()[$LINENO]($SECONDS): Failed to update the HACMPsecurity odm" >>$CLMGR_TMPLOG fi fi #=========================================================== # Set the "handle" attribute in the HACMPcluster class # to zero to indicate that an unsynced change exists in # the cluster configuration. #=========================================================== if (( rc == RC_SUCCESS )); then if [[ -n $level || -n $algorithm || -n $mechanism || -n $certificate || -n $private_key ]]; then print "HACMPcluster:handle=0" | odmchange -o HACMPcluster fi fi #=========================================================== : If output from this operation was requested, retrieve it #=========================================================== if (( $rc == RC_SUCCESS )); then if (( CLMGR_VERBOSE )) || [[ -n $CLMGR_ATTRS ]]; then CL=$LINENO KLIB_HACMP_get_cluster_attributes properties fi #================================================== : Update the clevents file with formatted event and : send an event upstream to the GUI server, if the : node has a running GUI agent on it #================================================== print "$0()[$LINENO]($SECONDS): cl_send_ui_event NODE \"$LOCAL_NODE\" SYNC PASSED PASSED \"A security configuration change was performed on cluster \\\"\$(CL=$LINENO KLIB_HACMP_get_cluster_name)\\\" at \$(date): $CLMGR_COMMAND\"" >>$CLMGR_TMPLOG cl_send_ui_event NODE "$LOCAL_NODE" SYNC PASSED PASSED "A security configuration change was performed on cluster \"$(CL=$LINENO KLIB_HACMP_get_cluster_name)\" at $(date): $CLMGR_COMMAND" print "$0()[$LINENO]($SECONDS): cl_send_ui_event RC: $?" >>$CLMGR_TMPLOG else rc=$RC_ERROR fi #======================================================================= : If a user input error was detected, provide some helpful suggestions #======================================================================= if (( $rc == RC_MISSING_INPUT || $rc == RC_INCORRECT_INPUT )) && \ [[ $CLMGR_GUI == *([[:space:]]) ]] then cl_dspmsg -s $CLMGR_SET $CLMGR_MSGS 104 "For more information about available options and syntax, try\n\"$HAUTILS/clmgr %1\$s\". As an\nalternative, if the PowerHA SystemMirror man pages have been installed, invoke\n\"$HAUTILS/clmgr -hv\" (or \"/usr/bin/man clmgr\"),\nsearching for \"%2\$s\" in the displayed text.\n\n" \ "modify cluster -h" "CLUSTER:" "$CLMGR_PROGNAME" 1>&2 fi log_return_msg "$rc" "$0()" "$LINENO" return $? } # End of "KLIB_HACMP_modify_cluster_security()" #============================================================================ # # Name: devDoc # # Description: This is a never-to-be-called, wrapper function that all the # clmgr FPATH functions implement in order to hide embedded # syntax from trace logging. This information is implemented # in POD format, and can be viewed in a number of ways using # POD tools. Some viewing suggestions for this function's POD- # formatted information are: # # perldoc # pod2text -c # pod2text -c --code # pod2html # # However, the more important use for this information is that # it is parsed by clmgr to display the syntax for this file's # operation. The information in the "SYNOPSIS" section is used # for this purpose. This feature was originally implemented # using the man page information. However, in a code review it # was pointed out that this approach had to be changed because # customers do not have to install the man pages! Therefore, a # built-in dependency on man page information would break the # automatic help feature of clmgr. So the SYNPOSIS section must # be used instead. # # IMPORTANT: As a result of this, it is imperative that the # information in this SYNOPSIS be kept in sync # with the man page information, which is owned # by the IDD team. # # Inputs: None. # # Outputs: None. # # Returns: n/a (not intended to be invoked) # #============================================================================ function devDoc { : <<'=cut' >/dev/null 2>&1 =head1 NAME KLIB_HACMP_modify_cluster_security =head1 SYNOPSIS clmgr manage cluster security \ [ LEVEL={Disable|Med} ] \ [ ALGORITHM={DES|3DES|AES} ]\ [ MECHANISM={OpenSSL|SSH} ] \ CERTIFICATE= \ PRIVATE_KEY= NOTE: if a "MECHANISM" of SSL or SSH is specified, then a custom made certificate and private key file must be provided. clmgr manage cluster security \ [ LEVEL={Disable|Med} ] \ [ ALGORITHM={DES|3DES|AES} ]\ [ MECHANISM="SelfSigned" ] \ [ CERTIFICATE= ] \ [ PRIVATE_KEY= ] NOTE: if a "MECHANISM" of Self-Signed is specified, then specifying a certificate and private key file is optional. If neither is provided, a default pair will be automatically generated. NOTE: the alias for "cluster" is "cl". =head1 DESCRIPTION Attempts to modify the cluster security configuration to conform to the provided specifications. =head1 ARGUMENTS 1. properties [REQUIRED] [hash ref] An associative array within which data about the modified object can be returned to the caller. 2. level [OPTIONAL] [string] The level of security for the cluster, from "Disable", "Med". 3. algorithm [OPTIONAL] [string] The symmetric algorithm to use, from "DES", "3DES", "AES". 4. mechanism [OPTIONAL] [string] Type security mechanism to auto-create and distribute, from "OpenSSL", "SelfSigned", "SSH". 5. certificate [OPTIONAL] [string] The absolute path to a certificate file. 6. private_key [OPTIONAL] [string] The absolute path to a private key file. =head1 RETURN 0: no errors were detected; the operation appears to have been successful 1: a general error has occurred 2: a specified resource does not exist, or could not be found 3: some required input was missing 4: some detected input was incorrect in some way 5: a required dependency does not exist 6: a specified search failed to match any data =cut } # End of POD-formatted documentation. #============================================================================== # The following, comment block attempts to enforce coding standards when this # file is edited via emacs or vim. This block _must_ appear at the very end # of the file, or the editor will not find it, and it will be ignored. #============================================================================== # Local Variables: # indent-tabs-mode: nil # tab-width: 4 # End: #============================================================================== # vim: tabstop=4 shiftwidth=4 expandtab #==============================================================================