#!/bin/bash # Copyright: 2023 Virtuozzo International GmbH # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # http://www.apache.org/licenses/LICENSE-2.0 # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. inherit default config os envinfo; inherit ${gw_boost_lib_inherit}; inherit extendperm; include virtuozzo vzexec; DESCRIPTION="Manipulate ssh keys, users"; VERSION="1" DEFAULT_ACTION="Usage"; $PROGRAM 'useradd'; $PROGRAM 'groupadd'; $PROGRAM 'getent'; $PROGRAM 'awk'; $PROGRAM 'sed'; $PROGRAM 'grep'; declare _UID; declare _GID; declare _USER_NAME="jelastic"; declare _homedir; declare _USER_EXISTS; declare _GROUP_EXISTS; declare _SSH_PRIVATE_KEY; declare _REFORM_MESSAGE; declare _SUPPLEMENTARY_GROUPS; declare _response_GUID; declare _REGENERATE_KEY="no"; declare _ACCESS_TYPE; declare _ACCESS_ACTION; _SSH_ACCESS_GROUP="ssh-access"; declare -r _JELASTIC_SHELL="/opt/jsh/jsh" VERBOSE=1; function onModLoadCallback() { [[ "$UID" != '0' ]] && { writeJSONResponseErr "result=>4034" "message=>You must be root!"; die -q; } return 0 } function doUsage() { showUsageMessage } function checkUserExists(){ local user=${1}; # response=$($GETENT passwd ${user}); response=$($GREP -E "^${user}:" /etc/passwd ); _USER_EXISTS=$?; log "Check user ${response}"; } function checkGroupExists(){ local group=${1}; response=$($GETENT group ${group}); _GROUP_EXISTS=$?; log "Check group ${response} $_GROUP_EXISTS"; } function groupId(){ local group=${1}; unset _response_GUID; log "groupId: ${group}"; [ -z "$group" ] && { writeJSONResponseErr "result=>4027" "message=>Cannot get GUID!"; die -q; }; checkGroupExists "${group}"; [ "$_GROUP_EXISTS" -ne 0 ] && { log "Adding group ${group}"; $GROUPADD "${group}" 2>&1 | $GRAB_OUTPUT ; result=$?; [ "$result" -ne 0 ] && { echo "ERROR adding group !!!!!!!!!!!!!!!!!!!!!!!!!"; } } _response_GUID=$($GETENT group ${group} | $AWK -F: '{ print $3 }'); } function __injected() { local CTID=$1 KEY [[ -z "${CTID}" ]] && { writeJSONResponseErr "result=>4105" "message=>Container not found" die -q } local pubkey result=0 $PROGRAM 'ssh-keygen' $PROGRAM 'vzctl' $PROGRAM 'hostname' # invalidate package db $VZCTL exec $CTID '[[ -f /etc/redhat-release ]] && rpm --rebuilddb &>/dev/null' #_DEFAULT_COMMENT comes from user-common.lib via include of $gw_boost_lib_inherit variable from metainf if [ -z "$_DEFAULT_COMMENT" ] ; then _DEFAULT_COMMENT="ssh-gate@jelastic.com" fi tmpfile=$(mktemp --dry-run) response=$($SSH_KEYGEN -C $_DEFAULT_COMMENT -P "" -q -f $tmpfile 2>&1) log "ssh-keygen response $response" $VZCTL exec $CTID 'if ! cd /root/.ssh 2>/dev/null >/dev/null; then mkdir -p /root/.ssh 2>/dev/null >/dev/null || rm /root && mkdir -p /root/.ssh; chmod 0700 /root/.ssh; fi' if [ -f "$tmpfile" ] ; then pubkey=$(cat "$tmpfile.pub") privkey=$(cat "$tmpfile") else result=4045 fi #fix permissions for existing key $VZCTL exec $CTID "[ -f /root/.ssh/id_rsa ] && chmod 400 /root/.ssh/id_rsa" if $VZCTL exec $CTID "[[ -f '/root/.ssh/id_rsa' ]]" && \ $VZCTL exec $CTID "ssh-keygen -l -f /root/.ssh/id_rsa" &>/dev/null && \ $VZCTL exec $CTID "ssh-keygen -y -P \"\" -C $_DEFAULT_COMMENT -f /root/.ssh/id_rsa &>/dev/null" && \ [[ "$_REGENERATE_KEY" != "yes" ]]; then $VZCTL exec $CTID "[[ -f /root/.ssh/authorized_keys ]]" && $VZCTL exec $CTID 'grep -q "$(ssh-keygen -y -f /root/.ssh/id_rsa)" /root/.ssh/authorized_keys' || \ $VZCTL exec $CTID "echo \"\$(ssh-keygen -y -f /root/.ssh/id_rsa) $_DEFAULT_COMMENT\" >> /root/.ssh/authorized_keys" else $VZCTL exec $CTID "[[ -f '/root/.ssh/authorized_keys' ]] && { $SED -i -re \"/${_DEFAULT_COMMENT}/d;/^s*$/d\" /root/.ssh/authorized_keys; chmod 0400 /root/.ssh/authorized_keys 2>>"$JEM_CALLS_LOG"; }" $VZCTL exec $CTID "echo -e \"\n${pubkey}\n\" >> /root/.ssh/authorized_keys" $VZCTL exec $CTID "echo -e \"${privkey}\" > /root/.ssh/id_rsa" $VZCTL exec $CTID "chmod 400 /root/.ssh/id_rsa"; fi $VZCTL exec $CTID "$SED -i -re 's/#?[Ss][Tt][Rr][Ii][Cc][Tt][Mm][Oo][Dd][Ee][Ss]\s+[Yy][Ee][Ss]/StrictModes no/g' /etc/ssh/sshd_config >/dev/null 2>&1" $VZCTL exec $CTID "service sshd reload >/dev/null 2>&1 || service ssh reload >/dev/null 2>&1" KEY="$($VZCTL exec $CTID 'cat /root/.ssh/id_rsa')" echo "{\"result\":\"${result}\",\"message\":\"$KEY\"}" rm "$tmpfile" rm "$tmpfile.pub" return 0; } function __processVDS(){ CTID=$1 inject doAdd 3i "return 0;" inject getAppUserInfo 3i "return 0;" inject doAdd 3i "__injected $CTID;" return 0 } function __injected_not_vds_add() { CTID=$1 if [ "x$_DISABLE_PASS_AUTH" == "xtrue" ] ; then local DISABLE_PASS_FLAG="-d"; fi vzexecSetCTID ${CTID} # invalidate package db vzexecRun '[[ -f /etc/redhat-release ]] && rpm --rebuilddb &>/dev/null' #vzctl exec ${_CTID} "jem user add" vzexecRun jem user add ${DISABLE_PASS_FLAG} if [ "$(vzexecGetLastErrCode)" -eq 0 ]; then _homedir=$(vzreadlink --vzroot="${prefix}" $(vzexecGetLastStdOut | $SED -rn 's/.*["]homedir["]:[[:blank:]]*["]([^"]+)["].*/\1/p')) DATA_OWNER="$(vzexecGetLastStdOut | $SED -rn 's/.*["]data_owner["]:[[:blank:]]*["]([^"]+)["].*/\1/p')" _UID=${DATA_OWNER/:*} _GUID=${DATA_OWNER/*:} [ -z "$_UID" -o -z "$_GUID" ] && { writeJSONResponseErr "result=>4026" "message=>Cannot get UID!"; die -q; }; if [ "x${COMPUTE_TYPE}" == "xgate" ] ;then return 0; fi bashRestricts 2>>"$JEM_CALLS_LOG" && vzexecGetLastStdOut && return 0 || { writeJSONResponseErr "result=>4119" "message=>Cannot fix user rights"; return 4119; } else writeJSONResponseErr "result=>4045" "message=>${vzexecGetLastStdErr}" fi } function __processNotVDS(){ CTID=$1 inject doAdd 3i "return 0;" inject getAppUserInfo 3i "return 0;" inject doAdd 3i "__injected_not_vds_add $CTID;" return 0 } function preAddCallback(){ log "$@"; while [ $# -gt 0 ] do case $1 in # for options with required arguments, an additional shift is required -d) _DISABLE_PASS_AUTH="true";; (--) shift; break;; -n|--new) _REGENERATE_KEY="yes";; (--) shift; break;; -e|--eid) _EID="$2"; shift;shift;; esac shift done log "Compute Type not gate." _USERADD_OPT=" -M "; _SUPPLEMENTARY_GROUPS="$_SSH_ACCESS_GROUP"; if [ ! -z "${_EID}" ]; then _CTID=$($SED -nr '/^([[:digit:]]+)<[/]ns1:veid>$/{s//\1/p; q}' "/var/opt/pva/agent/etc/configs/${_EID}" 2>/dev/null || :) [[ -z "${_CTID}" ]] && { writeJSONResponseErr "result=>4105" "message=>Container not found" die -q } # Call user add only for running container local waitCT=30 running isContainerRunning ${_CTID} ; running=$? while [ $running -ne 0 ] do waitCT=$((waitCT-1)) sleep 0.5 if [[ $waitCT == 0 ]]; then writeJSONResponseErr "result=>4110" "message=>Cannot execute command. Container is not running" ; die -q; fi isContainerRunning ${_CTID} ; running=$? done prefix=$($VZLIST $_CTID -Ho root) [[ -z "${prefix}" ]] && { writeJSONResponseErr "result=>4105" "message=>Container root dir not found" die -q } if [[ -f ${prefix}/etc/jelastic/metainf.conf ]]; then # JE-45748: evalueate compute type from metainf, empty value if there is no compute type in file eval "CT_$(grep "COMPUTE_TYPE=" ${prefix}/etc/jelastic/metainf.conf 2>/dev/null || echo "COMPUTE_TYPE=")" if [ ! -z $CT_COMPUTE_TYPE ] && [ "x$CT_COMPUTE_TYPE" != "xvds" ] && [ "x$CT_COMPUTE_TYPE" != "xubuntuvds" ] && [ "x$CT_COMPUTE_TYPE" != "xdebianvds" ] && [ "x$CT_COMPUTE_TYPE" != "xvzlinuxvps" ] && [ "x$CT_COMPUTE_TYPE" != "xalmalinuxvps" ] ; then # JE-45748 : container with metainf.conf and not empty compute type __processNotVDS ${_CTID} _UID=700 _GUID=700 else # JE-45748 : container with metainf.conf and empty compute type __processVDS ${_CTID} _UID=0 _GUID=0 fi else # JE-45748 : container without metainf.conf __processVDS ${_CTID} _UID=0 _GUID=0 fi fi isFunction "getAppUserInfo" && { log "Call getAppUserInfo"; "getAppUserInfo" "${_USER_NAME}"; } || { writeJSONResponseErr "result=>4048" "message=>Cannot get user info!"; die -q; }; [ -z "$_UID" -o -z "$_GUID" ] && { writeJSONResponseErr "result=>4026" "message=>Cannot get UID!"; die -q; }; } function doAdd() { log "detected user id $_UID"; log "detected group id $_GUID"; #check jelastic-ssh group and add if not exists [ -z "${_UID}" ] && { log "Cannot get UID"; writeJSONResponseErr "result=>4026" "message=>Cannot get UID!"; die -q; }; [ -z "${_GUID}" ] && { log "Cannot get GUID"; writeJSONResponseErr "result=>4027" "message=>Cannot get GUID!"; die -q; }; [ -z "${_homedir}" ] && { log "Cannot get homedir"; writeJSONResponseErr "result=>4025" "message=>Cannot get homedir!"; die -q; }; [ -z "${_USER_NAME}" ] && { log "Cannot get user name"; writeJSONResponseErr "result=>4024" "message=>Cannot get user name!"; die -q; }; checkUserExists "${_USER_NAME}"; [ ${_USER_EXISTS} -ne 0 ] && { [ ! -z "${_SUPPLEMENTARY_GROUPS}" ] && { local tmpgroup=$($SED 's/\,/ /g' <<< $_SUPPLEMENTARY_GROUPS); for group in $tmpgroup do log "$group"; checkGroupExists "${group}"; [ "$_GROUP_EXISTS" -ne 0 ] && { log "Adding group ${group}"; $GROUPADD "${group}" 2>&1 | $GRAB_OUTPUT ; result=$?; [ "$result" -ne 0 ] && { writeJSONResponseErr "result=>4044" "message=>ERROR adding group!"; die -q; }; } done _SUPPLEMENTARY_GROUPS=" -G "${_SUPPLEMENTARY_GROUPS}; } $USERADD -ou ${_UID} ${_USERADD_OPT} -g ${_GUID} -d ${_homedir} ${_SUPPLEMENTARY_GROUPS} ${_USER_NAME} 2>&1 | $GRAB_OUTPUT; checkUserExists ${_USER_NAME}; [ ${_USER_EXISTS} -ne 0 ] && { writeJSONResponseErr "result=>4045" "message=>ERROR adding user!"; die -q; }; } ExtendPerm ; if isFunction "postAddReform"; then "postAddReform" "${_USER_NAME}"; result_code=$?; if [ "$result_code" -eq 0 ]; then #writeJSONResponseOut "result=>0" "message=>${_SSH_PRIVATE_KEY}" echo "{\"result\":\"0\",\"message\":\"${_SSH_PRIVATE_KEY}\", \"homedir\":\"${_homedir}\", \"data_owner\":\"${_UID}:${_GUID}\"}" else writeJSONResponseErr "result=>4106" "message=>${_REFORM_MESSAGE}" return 1 fi else forceLog "No postAddReform" ; writeJSONResponseOut "result=>0" "message=>OK"; fi } function doAccess(){ while [ $# -gt 0 ] do case $1 in -t|--type) _ACCESS_TYPE=$2; shift ;; -a|--action) _ACCESS_ACTION=$2; shift ;; (--) shift; break;; esac shift done if [ "x${_ACCESS_TYPE}" == "xpwd" -a "x${_ACCESS_ACTION}" == "xdisable" ] then sshfile="/etc/ssh/sshd_config"; if $GREP -qE "^PasswordAuthentication.*$" "${sshfile}"; then $SED -i 's/^PasswordAuthentication.*$/PasswordAuthentication no/g' "${sshfile}" else if $GREP -qE "^\#PasswordAuthentication.*$" "${sshfile}"; then $SED -i 's/^\#PasswordAuthentication.*$/PasswordAuthentication no/g' "${sshfile}" else echo "PasswordAuthentication no" >> ${sshfile}; fi fi command -v systemctl >/dev/null 2>&1 && { systemctl --quiet is-enabled systemd-logind.service 2>/dev/null; isEnabled=$?; if [ $isEnabled -eq 0 ] ; then systemctl --quiet try-restart systemd-logind.service 2>/dev/null ; fi } reloadServiceSilent sshd writeJSONResponseOut "result=>0" "message=>Ok"; else writeJSONResponseOut "result=>0" "message=>Not implemented"; fi } function describeAdd() { echo "Add Jelastic user for GW" ; } function describeAccess() { echo "Enable/disabe ssh access" ; } function describeAddParameters() { echo "-e|--eid -n|--new -u|--uid "; } function describeAddOptions() { echo "-d: disables access by password"; echo "-e|--eid: environment id"; echo "-n|--new: regenerate keys"; echo "-u|--uid: user id"; } function describeAccessParameters() { echo "-t|--type -a|--action "; } function describeAccessOptions() { echo "-t|--type: access type"; echo "-a|--action: action (enable|disable)"; }