#!/bin/bash #============================================================================== # Copyright and license info is available in the LICENSE file included with # the Server Deployment Package (SDP), and also available online: # https://swarm.workshop.perforce.com/projects/perforce-software-sdp/view/main/LICENSE #------------------------------------------------------------------------------ #============================================================================== # Declarations and Environment set -u declare ThisScript=${0##*/} declare Version=1.0.3 declare ThisUser= declare LiveSudoersFile= declare TempSudoersFile= declare BackupSudoersFile= declare SDPInstance= declare P4Service= declare SystemCtlCmd= declare TmpFile= declare -i LimitedSudoers=2 declare -a SystemCtlCmdList declare Service= declare -i ErrorCount=0 declare -i i=0 declare -i NoOp=1 declare -i Force=0 declare Log= declare H1="==============================================================================" declare H2="------------------------------------------------------------------------------" #============================================================================== # Local Functions function msg () { echo -e "$*"; } function errmsg () { msg "\\nError: ${1:-Unknown Error}\\n"; ErrorCount+=1; } function bail () { errmsg "${1:-Unknown Error}"; exit "$ErrorCount"; } #------------------------------------------------------------------------------ # Function: terminate function terminate { # Disable signal trapping. trap - EXIT SIGINT SIGTERM [[ "$Log" != off ]] && \ msg "Log is: $Log\\n${H1}\\n" # With the trap removed, exit. exit "$ErrorCount" } #------------------------------------------------------------------------------ # Function: usage (required function) # # Input: # $1 - style, either -h (for short form) or -man (for man-page like format). # The default is -h. # # $2 - error message (optional). Specify this if usage() is called due to # user error, in which case the given message displayed first, followed by the # standard usage message (short or long depending on $1). If displaying an # error, usually $1 should be -h so that the longer usage message doesn't # obscure the error message. # # Sample Usage: # usage # usage -h # usage -man # usage -h "Incorrect command line usage." #------------------------------------------------------------------------------ function usage { declare style=${1:--h} declare errorMessage=${2:-Unset} if [[ "$errorMessage" != Unset ]]; then msg "\\n\\nUsage Error:\\n\\n$errorMessage\\n\\n" fi msg "USAGE for $ThisScript v$Version: $ThisScript {-full|-limited} [-y [-f]] [-L <log>] [-D] or $ThisScript [-h|-man] " if [[ $style == -man ]]; then msg " DESCRIPTION: This script generates a sudoers file for the OS user that owns /p4/common, which is expected to be the same user that the Perforce Helix Core service runs as (typically 'perforce'). By default, the sudoers file is generated for review. If the '-y' option is specified, the newly generated files is installed as the live sudoers file by copying to /etc/sudoers.d/<OSUSER> and adjusting permissions to 0400. If '-full' (full sudo) is specified, a one-line sudoers file is generated that looks something like this: perforce ALL=(ALL) NOPASSWD: ALL If '-limited' is specified, a limited sudoers file is generated granting only necessary access to the perforce user. If the sudoers file already exits, it will not be updated unless '-f' (force) is proivided. The limited sudoers is recommended for production deployments. OPTIONS: -full Specify '-full' to indicate that a sudoers file is to be generated granting full root access to the server machine. The '-full' or '-limited' option must be specified. This option is discouraged as it is not as secure as the '-limited' option. -limited Specify '-limited' to indicate that a sudoers file is to be generated granting limited access to the server machine. The '-full' or '-limited' option must be specified. This option is recommended for optimal security. -y This is confirmation to install the generated sudoers as the live sudoers file. -f Specify '-f' to overwite an existing limited sudoers file, /etc/sudoers.d/<OSUSER> -L <log> Specify the path to a log file, or the special value 'off' to disable logging. By default, all output (stdout and stderr) goes to: ${HOME:-/root}/${ThisScript%.sh}.<Datestamp>.log NOTE: This script is self-logging. That is, output displayed on the screen is simultaneously captured in the log file. -D Enable bash 'set -x' extreme debugging verbosity. HELP OPTIONS: -h Display short help message -man Display man-style help message EXAMPLES: EXAMPLE 1: Generate a limited sudoers file for review. cd /p4/sdp/Server/Unix/setup ./gen_sudoers.sh -limited EXAMPLE 2: Generate a limited sudoers file and install it. cd /p4/sdp/Server/Unix/setup ./gen_sudoers.sh -limited -y EXAMPLE 3: Generate a limited sudoers file and install it, replacing an existing one. cd /p4/sdp/Server/Unix/setup ./gen_sudoers.sh -limited -f -y EXAMPLE 4: Generate a full sudoers file and install it, replacing an cd /p4/sdp/Server/Unix/setup ./gen_sudoers.sh -full -f -y " fi exit 2 } #============================================================================== # Command Line Processing declare -i shiftArgs=0 set +u while [[ $# -gt 0 ]]; do case $1 in (-h) usage -h;; (-man) usage -man;; (-L) Log="$2"; shiftArgs=1;; (-f) Force=1;; (-full) LimitedSudoers=0;; (-limited) LimitedSudoers=1;; (-y) NoOp=0;; (-D) set -x;; # Debug; use 'set -x' mode. (-*) usage -h "Unknown flag ($1).";; (*) usage -h "Unknown parameter ($1).";; esac # Shift (modify $#) the appropriate number of times. shift; while [[ $shiftArgs -gt 0 ]]; do [[ $# -eq 0 ]] && usage -h "Incorrect number of arguments." shiftArgs=$shiftArgs-1 shift done done set -u #============================================================================== # Command Line Verification [[ -n "${Log:-}" ]] || \ Log="${HOME:-/root}/${ThisScript%.sh}.$(date +'%Y%m%d-%H%M%S').log" [[ "$LimitedSudoers" -eq 2 ]] && usage -h "Specify either '-limited' (limited sudoers) for '-full' (full sudoers)." #============================================================================== # Main Program trap terminate EXIT SIGINT SIGTERM if [[ "$Log" != off ]]; then touch "$Log" || bail "Couldn't touch log file [$Log]." # Redirect stdout and stderr to a log file. exec > >(tee "$Log") exec 2>&1 msg "${H1}\\nLog is: $Log" fi ThisUser=$(id -n -u) msg "Starting $ThisScript v$Version as $ThisUser@${HOSTNAME%%.*} on $(date)." [[ "$ThisUser" == root ]] || bail "Run this as root, not $ThisUser." # Determine list of SDP instances based in /p4/*/root dirs. i=0 # shellcheck disable=SC2012 for SDPInstance in $(ls -d /p4/*/logs/ 2>/dev/null|cut -d '/' -f 3); do P4ServiceList[i]="p4d_${SDPInstance}" i+=1 P4ServiceList[i]="p4broker_${SDPInstance}" i+=1 P4ServiceList[i]="p4p_${SDPInstance}" i+=1 P4ServiceList[i]="p4dtg_${SDPInstance}" i+=1 done SystemCtlCmdList[0]="start" SystemCtlCmdList[1]="stop" SystemCtlCmdList[2]="restart" SystemCtlCmdList[3]="status" SystemCtlCmdList[4]="cat" SystemCtlCmdList[5]="enable" SystemCtlCmdList[6]="disable" SystemCtlCmdList[7]="is-enabled" SystemCtlPath="$(command -v systemctl)" LslocksPath="$(command -v lslocks)" GetcapPath="$(command -v getcap)" SetcapPath="$(command -v setcap)" OSUSER="$(stat -c %U /p4/common 2>/dev/null)" [[ -n "$OSUSER" ]] || bail "Could not owner of /p4/common. Aborting." TempSudoersFile=$(mktemp) LiveSudoersFile="/etc/sudoers.d/$OSUSER" BackupSudoersFile="${HOME:-/root}/etc_sudoers.d_$OSUSER.bak.$(date +'%Y-%m-%d-%H%M%S')" if [[ "$LimitedSudoers" -eq 1 ]]; then { echo "Cmnd_Alias P4_SVC = \\" for Service in node_exporter p4prometheus; do for SystemCtlCmd in "${SystemCtlCmdList[@]}"; do echo " $SystemCtlPath $SystemCtlCmd $Service, \\" done done for P4Service in "${P4ServiceList[@]}"; do for SystemCtlCmd in "${SystemCtlCmdList[@]}"; do echo " $SystemCtlPath $SystemCtlCmd $P4Service, \\" done done echo " $GetcapPath, \\" echo " $SetcapPath, \\" echo -e " $LslocksPath\\n" echo "$OSUSER $HOSTNAME = (root) NOPASSWD: P4_SVC" } > "$TempSudoersFile" else echo "perforce ALL=(ALL) NOPASSWD: ALL" > "$TempSudoersFile" fi msg "Generated limited sudoers temp file:\\n${H2}\\n$(cat "$TempSudoersFile")\\n${H2}\\n" if [[ "$NoOp" -eq 0 ]]; then if [[ -r "$LiveSudoersFile" ]]; then msg "This file already exists: $LiveSudoersFile:\\n${H2}\\n$(cat "$LiveSudoersFile")\\n${H2}\\n" if diff -q "$TempSudoersFile" "$LiveSudoersFile" > /dev/null 2>&1; then msg "\\nVerified: The new generated sudoers file matches the currently installed one.\\nNo further processing required.\\n" exit 0 fi if [[ "$Force" -eq 1 ]]; then msg "\\nOverwriting existing live sudoers file due to -f." msg "Creating backup file [$BackupSudoersFile]." cp -p "$LiveSudoersFile" "$BackupSudoersFile" ||\ bail "Aborting: Failed to do: cp -p \"$LiveSudoersFile\" \"$BackupSudoersFile\"" else bail "Aborting because sudoers file already exists (displayed above).\\n\\nUse '-f' to replace this file with the newly generated one." fi fi msg "Installing $LiveSudoersFile" mv -f "$TempSudoersFile" "$LiveSudoersFile" ||\ bail "Failed to move generated temp file in place as: $LiveSudoersFile" chmod 0400 "$LiveSudoersFile" ||\ bail "Failed to do: chmod 0400 \"$LiveSudoersFile\"" TmpFile=$(mktemp) # shellcheck disable=SC2024 if sudo -l -U "$OSUSER" > "$TmpFile" 2>&1; then if grep -q 'syntax error' "$TmpFile"; then errmsg "The newly generated file failed a syntax check. Rolling back to prior version." mv -f "$BackupSudoersFile" "$LiveSudoersFile" ||\ bail "Failed to do: mv -f \"$BackupSudoersFile\" \"$LiveSudoersFile\"" bail "Aborted after successfull rollback to earlier version of [$LiveSudoersFile]." else msg "Newly generated sudoers file passed a syntax check." fi else bail "Could not test generated sudoers file. If needed, the backup file is: $BackupSudoersFile" fi msg "\\nNew sudoers file successfully installed.\\n" else msg "\\nBecause -y was not specified, not installing generated file as: $LiveSudoersFile" fi rm -f "$TempSudoersFile" exit "$ErrorCount"
# | Change | User | Description | Committed | |
---|---|---|---|---|---|
#2 | 30782 | C. Thomas Tyler |
Added new install_sdp.sh script and supporting documentation. The new install_sdp.sh makes SDP independent of the separate Helix Installer software (the reset_sdp.sh script). The new script greatly improves the installation experience for new server machines. It is ground up rewrite of the reset_sdp.sh script. The new script preserves the desired behaviors of the original Helix Installer script, but is focused on the use case of a fresh install on a new server machine. With this focus, the scripts does not have any "reset" logic, making it completely safe. Added various files and functionalityfrom Helix Installer into SDP. * Added firewalld templates to SDP, and added ufw support. * Improved sudoers generation. * Added bash shell templates. This script also installs in the coming SDP Package structure. New installs use a modified SDP structure that makes it so the /p4/sdp and /p4/common now point to folders on the local OS volume rather than the /hxepots volume. The /hxdepots volume, which is often NFS mounted, is still used for depots and checkpoints, and for backups. The new structure uses a new /opt/perforce/helix-sdp structure under which /p4/sdp and /p4/common point. This structure also contains the expaneded SDP tarball, downloads, helix_binaries, etc. This change represents the first of 3-phase rollout of the new package structure. In this first phase, the "silent beta" phase, the new structure is used for new installations only. This phase requires no changes to released SDP scripts except for mkdirs.sh, and even that script remains backward-compatible with the old structure if used independently of install_sdp.sh. If used with install_sdp.sh, the new structure is used. In the second phase (targeted for SPD 2024.2 release), the sdp_upgrade.sh script will convert existing installations to the new structure. In the third phase (targeted for SDP 2025.x), this script will be incorporated into OS pacakge installations for the helix-sdp package. Perforce internal wikis have more detail on this change. #review-30783 |
||
#1 | 30681 | C. Thomas Tyler |
Added gen_sudoers.sh script to generate a sudoers file for perforce OSUSER. This generates a more secure limited sudoers file. Previously, adding a sudoers entry for the OSUSER (usually 'perforce') was done only by the Helix Installer. In the Helix Installer variant, a single "one-size-filts-all" sudoers file was used, with the following characteristics: * The instances for Helix Core services were referenced with a '*' wildcard to match all SDP instances, which has since been determined to introduce a vulnerability. In this new script, the wildcard is replaced with separate entries for each SDP instance. * There were entries for all known paths of utilities like lslocks, setcap, and getcap. This new script generates the correct path valid for the current machine. With this change, the functionality will be available in the SDP directly. This new gen_sudoers.sh script can be called by mkdirs.sh directly to update the sudoers file each time a new SDP instance is added, if the new '-fs' (full sudo) or '-ls' (limited sudoers) entries are used. There is no change to the default behavior of mkdirs.sh; only a change if new options are utilized. This script comes with docs and examples for the new script as well as doc changes for mkdirs.sh. (Also added missing documentation for the '-no_enable' option). Further changes needed: * Add doc reference in SDP_Guide.Unix.adoc |