#!/bin/bash # --------------------------------------------------------------------- # Copyright © 2019-2023 Akeyless Security LTD. # # All rights reserved # ---------------------------------------------------------------------- set -e DEBUG_FILE=/dev/null function usage() { cat < -v [options] optional arguments: -i, --identity_file Selects a file from which the identity (private key) for public key authentication is read [default is '~/.ssh/id_rsa'] -c, --cert-issuer-name Akeyless certificate issuer name [mandatory] -l, --local-file File to copy [mandatory] -r, --remote-file File to copy [default is '~/'] -d, --direction Transfer direction, can be: upload/download [default is 'upload'] --profile Use a specific profile from your Akeyless CLI --ssh-extra-args Use to add official SSH arguments (except -i) EOF exit 1 } function build_tunnel() { REMOTE_USERNAME=$1 REMOTE_HOST=$2 REMOTE_PORT=$3 PROXY_HOST=$4 PROXY_PORT=$5 ITEM_NAME=$6 LOCAL_PORT=$7 SSH_CERT_ISSUER=$8 ${AKEYLESS_CLI} connect -t ${REMOTE_USERNAME}@${REMOTE_HOST}:${REMOTE_PORT} --name "${ITEM_NAME}" -v ${PROXY_HOST}:${PROXY_PORT} -c "${SSH_CERT_ISSUER}" \ --tunnel="-L ~/.ssh/akeyless-ssh-agent:/tmp/akeyless-ssh-agent -L ${LOCAL_PORT}:${REMOTE_HOST}:${REMOTE_PORT}" --ssh-extra-args='-tt' --profile ${profile} > ${DEBUG_FILE} 2>&1 & TUNNEL_PID=$! echo $TUNNEL_PID } ( [ "$2" != "via" -a "$2" != "-v" ] || [ "$3" == "" ] ) && usage identity_file="" cert_issuer_name="" profile="default" remote_server=$1 jumpbox_server=$3 local_file="" remote_file="~/" direction="upload" seen_local=no legacy_signing_alg=true ssh_extra_args="-4" AKEYLESS_CLI=akeyless DISPLAY_STAGES=yes item_name="" [ -e ~/.akeyless-connect.rc ] && source ~/.akeyless-connect.rc [ "$IDENTITY_FILE" != "" ] && identity_file="$IDENTITY_FILE" [ "$CERT_ISSUER_NAME" != "" ] && cert_issuer_name="$CERT_ISSUER_NAME" [ "$AKEYLESS_PROFILE" != "" ] && profile="$AKEYLESS_PROFILE" [ "$AKEYLESS_GW_REST_API" != "" ] && AKEYLESS_API_GW="$AKEYLESS_GW_REST_API" [ "$BASTION_API_PREFIX" != "" ] && BASTION_API_PREFIX_="$BASTION_API_PREFIX" [ "$BASTION_API_PATH" != "" ] && BASTION_API_PATH_="$BASTION_API_PATH" [ "$BASTION_API_PROTO" != "" ] && BASTION_API_PROTO_="$BASTION_API_PROTO" [ "$BASTION_API_PORT" != "" ] && BASTION_API_PORT_="$BASTION_API_PORT" [ "$SSH_EXTRA_ARGS" != "" ] && ssh_extra_args="$SSH_EXTRA_ARGS" [ -e ~/.akeyless-sphere.rc ] && source ~/.akeyless-sphere.rc until [ -z $4 ]; do case $4 in --profile ) profile="$5" shift ;; -i | --identity_file ) identity_file="$5" shift ;; -l | --local-file ) local_file="$5" seen_local=yes shift ;; -r | --remote-file ) remote_file="$5" [ "$seen_local" == "no" ] && direction="download" shift ;; -d | --direction ) direction=`echo $5 | tr '[:upper:]' '[:lower:]'` shift ;; --ssh-extra-args ) ssh_extra_args="$5" shift ;; -c | --cert-issuer-name ) cert_issuer_name="$5" shift ;; --name ) item_name="$5" shift ;; esac shift done if [ "${cert_issuer_name}" == "" ] || [ "${local_file}" == "" ]; then [ "${cert_issuer_name}" == "" ] && echo -n "cert_issuer_name " || echo -n "local_file " echo "parameter is missing" usage fi if [ "${direction}" == "download" ] && [ "${remote_file}" == "" ]; then echo "remote-file parameter is missing" usage fi BASTION_API_PORT_=${BASTION_API_PORT_:-9900} BASTION_API_PATH_=${BASTION_API_PATH_:-""} BASTION_API_PREFIX_=${BASTION_API_PREFIX_:-""} BASTION_API_PROTO_=${BASTION_API_PROTO_:-http} ID_RSA_KEY=${identity_file:-~/.ssh/id_rsa} function get_ssh_certificate() { CERT_USERNAME=$1 ISSUER_NAME=$2 PUB_KEY_FILE=$3 TMP_PROFILE=$4 if [ "$AKEYLESS_API_GW" == "" ]; then [ "$DISPLAY_STAGES" == "yes" ] && echo "Issuing SSH Certificate..." ${AKEYLESS_CLI} get-ssh-certificate --cert-username "${CERT_USERNAME}" --cert-issuer-name "${ISSUER_NAME}" --public-key-file-path "${PUB_KEY_FILE}" --legacy-signing-alg-name=${legacy_signing_alg} --profile "${TMP_PROFILE}" > /dev/null else if [ "$API_TOKEN" == "" ]; then ${AKEYLESS_CLI} list-items --filter akeyless-sphere --profile ${profile} >& /dev/null JWT=`cat ~/.akeyless/.tmp_creds/${profile}*` ACCESS_ID=`grep -E "admin_email|access_id" ~/.akeyless/profiles/${TMP_PROFILE}*.toml | tail -n 1 | cut -d '"' -f 2` API_TOKEN=`curl -s -d "cmd=static-creds-auth&access-id=${ACCESS_ID}" --data-urlencode "creds=${JWT}" ${AKEYLESS_API_GW} | grep token | cut -d '"' -f 4` if [[ $API_TOKEN != "t-"* ]]; then ACCESS_ID=`grep access_id ~/.akeyless/profiles/${profile}.toml | cut -d '"' -f 2` ACCESS_TYPE=`grep access_type ~/.akeyless/profiles/${profile}.toml | cut -d '"' -f 2` API_TOKEN=`${AKEYLESS_CLI} auth --access-id ${ACCESS_ID} --access-type ${ACCESS_TYPE} | grep '^Token:' | awk '{print $2}'` fi if [[ $API_TOKEN != "t-"* ]]; then echo "ERROR [Unable to retrieve a valid token]" exit 1 fi fi [ "$DISPLAY_STAGES" == "yes" ] && echo "Issuing SSH Certificate..." PUB_KEY_DATA=`cat ${PUB_KEY_FILE}` CERT_FILE=`echo "$PUB_KEY_FILE" | sed 's/.pub$/-cert.pub/'` curl -s -d "cmd=get-ssh-certificate&cert-username=${CERT_USERNAME}&cert-issuer-name=${ISSUER_NAME}&legacy-signing-alg-name=${legacy_signing_alg}" --data-urlencode "token=${API_TOKEN}" --data-urlencode "public-key-data=${PUB_KEY_DATA}" ${AKEYLESS_API_GW} | grep response -A 2 | tail -n 1 | cut -d '"' -f 2 > ${CERT_FILE} fi } function is_tunnel_ready() { CHK_PORT=$1 (echo >/dev/tcp/127.0.0.1/$CHK_PORT) &>/dev/null && echo "ready" || true } if [ ! -f "${ID_RSA_KEY}" ]; then echo "Can't find SSH keypair, would you like to create one?" select yn in "Yes" "No"; do case $yn in Yes) ssh-keygen -t rsa -f $HOME/.ssh/id_rsa; break;; No) exit 1;; esac done fi USERNAME_=`echo "$remote_server" | cut -d '@' -f 1` REMOTE_SRV_=`echo "$remote_server" | cut -d '@' -f 2` JB_SRV_=`echo "$jumpbox_server" | sed 's/:.*//g'` JB_PORT_=`echo "$jumpbox_server" | sed 's/.*://g'` RM_SRV_=`echo ${REMOTE_SRV_} | sed 's/:.*//g'` RM_PORT_=`echo $remote_server | cut -d '@' -f 2 | sed 's/.*://g'` [ "${RM_PORT_}" == "${REMOTE_SRV_}" ] && RM_PORT_=22 [ "${JB_PORT_}" == "${JB_SRV_}" ] && JB_PORT_=22 [ "$DISPLAY_STAGES" == "yes" ] && echo "Configuring Temporary Session..." RES_=$(curl -s -d "cmd=configure&access-id=${USERNAME_}&remote-server=${RM_SRV_}:${RM_PORT_}" ${BASTION_API_PROTO_}://"${BASTION_API_PREFIX_}${JB_SRV_}${BASTION_API_PATH_}":"${BASTION_API_PORT_}") if [ -z "$RES_" ]; then echo "Failed to configure temporary session" exit 1 fi STATUS_=`echo ${RES_} | tr ',' '\n' | grep status | cut -d '"' -f 4` if [ "${STATUS_}" == "success" ]; then GEN_USER=`echo ${RES_} | tr ',' '\n' | grep response | tail -n 1 | cut -d '"' -f 4` [ "$GEN_USER" == "" ] && GEN_USER=`echo ${RES_} | tr ',' '\n' | grep response -A 1 | tail -n 1 | cut -d '"' -f 2` cp -f ${ID_RSA_KEY}.pub ${ID_RSA_KEY}_akeyless.pub cp -f ${ID_RSA_KEY} ${ID_RSA_KEY}_akeyless [ "$DISPLAY_STAGES" == "yes" ] && echo "Authenticating..." get_ssh_certificate "${GEN_USER}" "${cert_issuer_name}" "${ID_RSA_KEY}".pub "${profile}" get_ssh_certificate "${USERNAME_}" "${cert_issuer_name}" "${ID_RSA_KEY}"_akeyless.pub "${profile}" if [ "$item_name" != "" ]; then LOCAL_PORT=26879 export SSH_AUTH_SOCK=~/.ssh/akeyless-ssh-agent # delete the socket file rm -f $SSH_AUTH_SOCK [ "$DISPLAY_STAGES" == "yes" ] && echo -n "Building Tunnel." TUNNEL_PID=$(build_tunnel "${USERNAME_}" "${RM_SRV_}" "${RM_PORT_}" "${JB_SRV_}" "${JB_PORT_}" "${item_name}" "${LOCAL_PORT}" "${cert_issuer_name}") for i in {1..25}; do \ sleep 1 TUNNEL_READY=`is_tunnel_ready $LOCAL_PORT` if [ "$TUNNEL_READY" != "" ]; then [ "$DISPLAY_STAGES" == "yes" ] && echo " [DONE]" break else [ "$DISPLAY_STAGES" == "yes" ] && echo -n "." fi done [ "$DISPLAY_STAGES" == "yes" ] && echo RM_SRV_="127.0.0.1" #change the target host to localhost because of the tunnel if [ "${direction}" == "download" ]; then [ "$DISPLAY_STAGES" == "yes" ] && echo "Downloading [${USERNAME_}@${RM_SRV_}:${RM_PORT_}:${remote_file}]->[${local_file}]..." scp -i "${ID_RSA_KEY}"_akeyless -i "${ID_RSA_KEY}" -o "UserKnownHostsFile=/dev/null" -o "StrictHostKeyChecking=no" -o ConnectTimeout=15 "$ssh_extra_args" -P "${LOCAL_PORT}" "${USERNAME_}"@"${RM_SRV_}":"${remote_file}" "${local_file}" else [ "$DISPLAY_STAGES" == "yes" ] && echo "Uploading [${local_file}]->[${USERNAME_}@${RM_SRV_}:${RM_PORT_}:${remote_file}]..." scp -i "${ID_RSA_KEY}"_akeyless -i "${ID_RSA_KEY}" -o "UserKnownHostsFile=/dev/null" -o "StrictHostKeyChecking=no" -o ConnectTimeout=15 "$ssh_extra_args" -P "${LOCAL_PORT}" "${local_file}" "${USERNAME_}"@"${RM_SRV_}":"${remote_file}" fi # kill all child processes of tunnel kill -9 $(ps -ef | grep -E "\-[L|P] $LOCAL_PORT" | awk '{print $2}') 2>/dev/null # kill tunnel process kill -9 ${TUNNEL_PID} 2>/dev/null # delete the socket file rm -f $SSH_AUTH_SOCK else if [ "${direction}" == "download" ]; then [ "$DISPLAY_STAGES" == "yes" ] && echo "Downloading [${USERNAME_}@${RM_SRV_}:${RM_PORT_}:${remote_file}]->[${local_file}]..." scp -i "${ID_RSA_KEY}"_akeyless -i "${ID_RSA_KEY}" -o ConnectTimeout=15 "$ssh_extra_args" -P "${RM_PORT_}" -o "ProxyJump ${GEN_USER}@${JB_SRV_}:${JB_PORT_}" "${USERNAME_}"@"${RM_SRV_}":"${remote_file}" "${local_file}" else [ "$DISPLAY_STAGES" == "yes" ] && echo "Uploading [${local_file}]->[${USERNAME_}@${RM_SRV_}:${RM_PORT_}:${remote_file}]..." scp -i "${ID_RSA_KEY}"_akeyless -i "${ID_RSA_KEY}" -o ConnectTimeout=15 "$ssh_extra_args" -P "${RM_PORT_}" -o "ProxyJump ${GEN_USER}@${JB_SRV_}:${JB_PORT_}" "${local_file}" "${USERNAME_}"@"${RM_SRV_}":"${remote_file}" fi fi rm "${ID_RSA_KEY}"_akeyless "${ID_RSA_KEY}"_akeyless.pub >& /dev/null else echo "ERROR [$STATUS_]" exit 1 fi