Knowing If a Remote Port Forward Was Successful

Knowing if a remote port forward was successful?

Try adding -o ExitOnForwardFailure=yes

The full command will be something like:

ssh -N -R [port_XX]:localhost:22 -o ExitOnForwardFailure=yes user@host

(-N is useful to just forward port, without opening a remote shell; you can also add -f to send process in background, no need if you are running under screen, though..)

How to make SSH remote port forward that listens 0.0.0.0

Enable GatewayPorts in sshd_config (by default it is disabled). Enabling it will instruct sshd to allow remote port forwardings to bind to a non-loopback address. AskUbuntu has a similar question about Reverse Port Tunneling that goes into more details.

kubectl - determine local port used when port-forwarding on random local port?

I couldn't get any other approach to work, so I ended writing a script that dynamically finds a free port, and sets up a forward, then returns the port. If a matching port forward already exists, it can return that instead. Sample usage below. Note that currently I use another script to determine a free local port, and then use that port number to setup the port forward, and could've instead parsed the output from the generic port-forward command I mentioned in the question, but this approach is less fragile, and more re-usable, I believe.

$ kpf help

kubernetes port forward helper
* helps you transparently ensure a port forward is setup for a service.

usage: kpf [require|get|create|remove|remove_all] {SERVICEI} {REMOTE_PORT} {EXTRA_ARGS}

e.g.

# get or create a port forward for your-service on remote port 8080
# returns the port if successful, or fails and shows an error message
kpf require service/your-service 8080

# return any existing port forwards for your-service on remote port 8080, or nothing
kpf get service/your-service 8080

# create a new local port forward for your-service on remote port 8080, returning port used
kpf create service/your-service 8080

# remove any port forwards found to your-service on remote port 8080
kpf remove service/your-service 8080

# remove ALL port forwards to any kubernetes remote ports
kpf remove_all

kpf

#!/usr/bin/env bash
DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )"; PATH="$DIR:$PATH"
ME=$(basename "$0")

function show_help()
{
IT=$(cat <<EOF

kubernetes port forward helper
* helps you transparently ensure a port forward is setup for a service.

usage: $ME [require|get|create|remove|remove_all] {SERVICE} {REMOTE_PORT} {EXTRA_ARGS}

e.g.

# get or create a port forward for your-service on remote port 8080
# returns the port if successful, or fails and shows an error message
$ME require service/your-service 8080

# return any existing port forwards for your-service on remote port 8080, or nothing
$ME get service/your-service 8080

# create a new local port forward for your-service on remote port 8080, returning port used
$ME create service/your-service 8080

# remove any port forwards found to your-service on remote port 8080
$ME remove service/your-service 8080

# remove ALL port forwards to any kubernetes remote ports
$ME remove_all

# create a port forward on local port 4569, for localstack, in namespace localstack
$ME create svc/localstack 4569:4569 --namespace localstack
EOF
)
echo "$IT"
echo
exit
}

if [ -z "$1" ]
then
show_help
fi
if [ "$1" = "help" ] || [ "$1" = '?' ] || [ "$1" = "--help" ] || [ "$1" = "h" ]; then
show_help
fi

OP=$1
SERVICE=$2
REMOTE_PORT=$3
if [ -n "$4" ]
then
shift;
shift;
shift;
EXTRA_ARGS="$@"
fi

LOG_FILE_DIR="/tmp/__${ME}"

function getExistingPortForward(){
listPortForwards | grep "$SERVICE" | grep -e ":$REMOTE_PORT" | head -n1
}

function coln(){
COL=$1
DELIM=${2:-' '}
awk -F"$DELIM" -v col="$COL" '{print $col}' | sort | uniq
}

function err(){
echo "$@" >&2;
}

function isNumeric(){
INPUT=$*
case ${INPUT#[-+]} in
*[!0-9]* ) echo NO ;;
* ) echo YES ;;
esac
}

function getPid(){
local INFO=$(getExistingPortForward | awk '{print $2}')

if [ "$(isNumeric "$INFO")" == "YES" ]; then
echo "$INFO"
fi
}

function getLocalPort(){
local INFO=$(getExistingPortForward)
INFO=$(echo "$INFO" | awk -F ":$REMOTE_PORT" '{print $1}' | awk '{print $NF}')

if [ "$(isNumeric "$INFO")" == "YES" ]; then
echo "$INFO"
fi
}

function removePortForward(){
local PID=$(getPid)
if [ -z "$PID" ]
then
return;
fi

set -x
kill "$PID"
}

function portForward(){
local LOCAL_PORT=$1
local FILE=$2

kubectl port-forward "$SERVICE" "$LOCAL_PORT":"$REMOTE_PORT" $EXTRA_ARGS > $FILE 2>&1 &
}

function failIfFileContains(){
local FILE=$1
local SUBSTR=$2
local MSG=$3
if grep -iq "$SUBSTR" "$FILE"; then
cat "$FILE" >&2
err "Failed: $MSG"
err "Service:$SERVICE, $REMOTE_PORT $EXTRA_ARGS"
err
exit 1
fi
}

function getTargetLocalPort(){
local RESULT
if [[ $REMOTE_PORT == *":"* ]]; then
RESULT=$(echo $REMOTE_PORT | coln 1 ':')
REMOTE_PORT=$(echo $REMOTE_PORT | coln 2 ':')
else
RESULT=$(port_find_free_local_port)
fi
echo "$RESULT"
}

function createPortForward(){
local LOCAL_PORT=$(getTargetLocalPort)
local LOG_FILE="${LOG_FILE_DIR}/${LOCAL_PORT}_log.txt"
mkdir -p "$LOG_FILE_DIR"
rm -f "$LOG_FILE"

portForward "$LOCAL_PORT" "$LOG_FILE"

# wait for the log file to indicate success or failure
while true
do
sleep 0.5
failIfFileContains "$LOG_FILE" "address already" "Port $LOCAL_PORT already in use"
failIfFileContains "$LOG_FILE" "must be logged" "Please set some PCSK variables, and try again"
failIfFileContains "$LOG_FILE" "Failed" "Please review the above log, and try again"
failIfFileContains "$LOG_FILE" "Error" "Please review the above log, and try again"
if grep -q Forwarding "$LOG_FILE"; then
# port forward successful
echo "$LOCAL_PORT"
break
fi
done
}

function getOrCreatePortForward(){
local PORT=$(getLocalPort)
if [ -n "$PORT" ]
then
echo "$PORT"
exit;
fi

createPortForward
}

function listPortForwards(){
ps aux | grep kubectl | grep port-forward | sort
}

function removeAll(){
listPortForwards | awk '{print $2}' | xargs kill -9
rm -fr "$LOG_FILE_DIR"
}

function require3Args(){
if [ -z "$REMOTE_PORT" ]
then
show_help
fi
}

if [ "$OP" = "get" ]; then
require3Args
getLocalPort
elif [ "$OP" = "create" ]; then
require3Args
createPortForward
elif [ "$OP" = "require" ]; then
require3Args
getOrCreatePortForward
elif [ "$OP" = "remove" ]; then
require3Args
removePortForward
elif [ "$OP" = "list" ]; then
listPortForwards
elif [ "$OP" = "remove_all" ]; then
removeAll
else
show_help
fi

port_find_free_local_port

#!/usr/bin/env python
# https://stackoverflow.com/a/45690594/26510

import socket
from contextlib import closing

def find_free_port():
with closing(socket.socket(socket.AF_INET, socket.SOCK_STREAM)) as s:
s.bind(('', 0))
s.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
return s.getsockname()[1]

print(find_free_port())


Related Topics



Leave a reply



Submit