#!/bin/bash

# ---------------------------------------------------------------------
# Copyright © 2019-2023  Akeyless Security LTD.
#
# All rights reserved
# ----------------------------------------------------------------------

set -e

DEBUG_FILE=/dev/null

function usage() {
	cat <<EOF
Usage: $0 <user@remote-server[:port]> -v <jumpbox-server[:port]> [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