From 02a6ce88f6609b88762c578ce2bbe2019290cd8d Mon Sep 17 00:00:00 2001 From: Kevin Pedro Date: Mon, 4 Aug 2025 20:11:51 -0500 Subject: [PATCH 1/3] add tunn utility for ssh tunnels with documentation --- README.md | 69 +++++++++++ docs/foxyproxy_lpc_generic.png | Bin 0 -> 12442 bytes tunn | 217 +++++++++++++++++++++++++++++++++ 3 files changed, 286 insertions(+) create mode 100755 docs/foxyproxy_lpc_generic.png create mode 100755 tunn diff --git a/README.md b/README.md index b608c36..a863e49 100644 --- a/README.md +++ b/README.md @@ -189,6 +189,75 @@ In this particular case, it is necessary to upgrade `pip` because the Python ver **NOTE**: These recipes only install the bindings for Python3. (Python2 was still the default in `CMSSW_10_6_X`.) You will need to make sure any scripts using the bindings are compatible with Python3. +## `tunn` + +A simple utility to create and manage SSH tunnels. + +The basic usage of `tunn` follows this pattern: +1. `tunn make user@cmslpc-sl9.fnal.gov` +2. `tunn list`: + ``` + index: socket port command + 0: "/home/[user]/.tsock_xyz" 8XXX "user@cmslpc-sl9.fnal.gov" + ``` +3. `tunn kill 0`: + ``` + Exit request sent. + ``` + +If you have host aliases defined in your `~/.ssh/config` file, you can use them with `tunn`. + +### Detailed usage + +The configuration and command-line options for `tunn` are described in its usage message: +``` +tunn [operation] [options] [arguments] + +Default settings are obtained from the config file at /home/[user]/.tunnconfig. +To override the config file location, put this in your .bashrc or other login file: + export TUNN_CONFIG=/my/preferred/file +The available config variables are: TUNN_PREFIX, TUNN_PORT, TUNN_VERBOSE. +Their values should be specified in the config file using bash syntax, e.g.: + TUNN_PORT=8XXX +(If TUNN_PORT is not specified in the config file or via the command line option, +the default value is taken from the last three digits of your UID.) + +Operations: + +make make new tunnel + -n [name] tunnel socket name prefix (default: /home/[user]/.tsock) + -p [port] tunnel port (default: 8XXX) + [destination] ssh destination for tunnel (required) + +list list open tunnels + +kill kill specified tunnel + [index] index of tunnel (required) + +Common options: +-u (unclean) do not auto-remove closed tunnels from list +-v toggle verbosity (default: false) +-h print this message and exit +``` + +### Web browser usage + +There are addons available for web browsers to route traffic through ssh tunnels. +[FoxyProxy](https://getfoxyproxy.org/downloads/) is recommended for most browsers (Chrome, Firefox, and derivatives; Safari has [equivalent internal settings](https://help.getfoxyproxy.org/index.php/knowledge-base/how-to-use-your-proxy-services-with-safari/).) + +You can add LPC as a proxy server in the "Proxies" tab, with settings as follows: +* Title: LPC +* Hostname: localhost +* Type: SOCKS5 +* Port: [your TUNN_PORT value 8XXX] +* Proxy by patterns: [blank], "Include", "Wildcard", Title: FNAL, Pattern: \*://\*.fnal.gov/ + +
+Firefox example screenshot: + +![Foxyproxy settings screenshot](./docs/foxyproxy_lpc_generic.png) +
+ ## Unit and Integration testing ### Automated diff --git a/docs/foxyproxy_lpc_generic.png b/docs/foxyproxy_lpc_generic.png new file mode 100755 index 0000000000000000000000000000000000000000..063d489a200face4f69ba5baf6d0b4e6752ac6a0 GIT binary patch literal 12442 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 literal 0 HcmV?d00001 diff --git a/tunn b/tunn new file mode 100755 index 0000000..5cfd54d --- /dev/null +++ b/tunn @@ -0,0 +1,217 @@ +#!/bin/bash + +# helper functions + +tunn_check_op() { + if [ "$TUNN_OP" != "$1" ]; then + usage 1 + fi +} + +tunn_echo() { + if [ "$TUNN_VERBOSE" == "true" ]; then + echo "$@" + fi +} + +tunn_read_impl() { + # get list of tunnels + readarray -t TUNN_LIST < "$TUNN_LISTFILE" + export TUNN_LIST +} + +tunn_update() { + tunn_read_impl + # clear list of tunnels + : > "$TUNN_LISTFILE" + # write open tunnels back into list + for ((i=0; i < ${#TUNN_LIST[@]}; i++)); do + tunn_entry $i + if ssh $TUNN_DEST -S "$TUNN_SOCKETNAME" -O check >&/dev/null; then + echo "${TUNN_LIST[$i]}" >> "$TUNN_LISTFILE" + else + tunn_echo "Removing dead tunnel: ${TUNN_LIST[$i]}" + rm "$TUNN_SOCKETNAME" >&/dev/null + fi + done + unset TUNN_LIST +} + +tunn_read() { + if [ -z "$TUNN_UNCLEAN" ]; then + tunn_read_impl + tunn_update + fi + tunn_read_impl +} + +tunn_entry() { + ENTRY="${TUNN_LIST[$1]}" + # split, respecting quotes + declare -a "ENTRY_ARR=($ENTRY)" + + # reconstruct command based on output format + export TUNN_SOCKETNAME="${ENTRY_ARR[0]}" + export TUNN_PORT="${ENTRY_ARR[1]}" + export TUNN_DEST="${ENTRY_ARR[2]}" +} + +# operations + +tunn_make() { + TUNN_DEST="$@" + if [ -z "$TUNN_DEST" ]; then + usage 1 + fi + + # generate a random name for socket + TUNN_SOCKETNUM=$(uuidgen) + TUNN_SOCKETNAME="$TUNN_PREFIX"_"$TUNN_SOCKETNUM" + + # execute tunnel command + ssh $TUNN_DEST -N -D $TUNN_PORT -f -M -S "$TUNN_SOCKETNAME" 2>/dev/null + TUNN_EXIT=$? + + # add to list of tunnels + if [ $TUNN_EXIT -eq 0 ]; then + echo "\"$TUNN_SOCKETNAME\" $TUNN_PORT \"$TUNN_DEST\"" >> "$TUNN_LISTFILE" + tunn_echo "Tunnel ready, port: $TUNN_PORT" + else + echo "Could not create tunnel (exit code $TUNN_EXIT)" + exit $TUNN_EXIT + fi +} + +tunn_list() { + if [ -n "$1" ]; then + usage 1 + fi + + tunn_read + if [ ${#TUNN_LIST[@]} -eq 0 ]; then + return + fi + echo "index: socket port destination" + for ((i=0; i < ${#TUNN_LIST[@]}; i++)); do + echo "$i: ${TUNN_LIST[$i]}" + done +} + +tunn_kill() { + INDEX="$1" + if [ -z "$INDEX" ]; then + usage 1 + fi + + # get this tunnel + tunn_read + if [ $INDEX -lt ${#TUNN_LIST[@]} ]; then + tunn_entry $INDEX + else + echo "Tunnel index $INDEX not found" + exit 2 + fi + + # execute kill command + ssh $TUNN_DEST -S "$TUNN_SOCKETNAME" -O exit + tunn_echo "Killed tunnel: ${TUNN_LIST[$INDEX]}" + + # update list + sed -i "$((INDEX+1))"d "$TUNN_LISTFILE" +} + +# defaults +TUNN_LISTFILE=~/.tunnlist +: ${TUNN_CONFIG:=~/.tunnconfig} +TUNN_SOCKETNAME="" +TUNN_DEST="" +TUNN_INDEX="" +TUNN_UNCLEAN="" + +# get config defaults +if [ -e "$TUNN_CONFIG" ]; then + source "$TUNN_CONFIG" +fi +: ${TUNN_PREFIX:=~/.tsock} +UTMP=$(id -u) +UTMP=${UTMP:0-3} +: ${TUNN_PORT:=8${UTMP}} +: ${TUNN_VERBOSE:=false} + +declare -A TUNN_INVERT +TUNN_INVERT[true]=false +TUNN_INVERT[false]=true + +usage() { + ECHO="echo -e" + $ECHO "tunn [operation] [options] [arguments]" + $ECHO + $ECHO "Default settings are obtained from the config file at ${TUNN_CONFIG}." + $ECHO "To override the config file location, put this in your .bashrc or other login file:" + $ECHO "\texport TUNN_CONFIG=/my/preferred/file" + $ECHO "The available config variables are: TUNN_PREFIX, TUNN_PORT, TUNN_VERBOSE." + $ECHO "Their values should be specified in the config file using bash syntax, e.g.:" + $ECHO "\tTUNN_PORT=8XXX" + $ECHO "(If TUNN_PORT is not specified in the config file or via the command line option," + $ECHO "the default value is taken from the last three digits of your UID.)" + $ECHO + $ECHO "Operations:" + $ECHO + $ECHO "make \t make new tunnel" + $ECHO "\t-n [name] \t tunnel socket name prefix (default: ${TUNN_PREFIX})" + $ECHO "\t-p [port] \t tunnel port (default: $TUNN_PORT)" + $ECHO "\t[destination] \t ssh destination for tunnel (required)" + $ECHO + $ECHO "list \t list open tunnels" + $ECHO + $ECHO "kill \t kill specified tunnel" + $ECHO "\t[index] \t index of tunnel (required)" + $ECHO + $ECHO "Common options:" + $ECHO "-u \t (unclean) do not auto-remove closed tunnels from list" + $ECHO "-v \t toggle verbosity (default: $TUNN_VERBOSE)" + $ECHO "-h \t print this message and exit" + exit $1 +} + +# get operation +TUNN_OP=$1 +shift 1 + +TUNN_OPFN="" +case "$TUNN_OP" in + make) TUNN_OPFN=tunn_make + ;; + list) TUNN_OPFN=tunn_list + ;; + kill) TUNN_OPFN=tunn_kill + ;; + *) usage 1 + ;; +esac + +while getopts "n:p:uvh" opt; do + case "$opt" in + n) tunn_check_op make; TUNN_PREFIX="$OPTARG" + ;; + p) tunn_check_op make; TUNN_PORT="$OPTARG" + ;; + u) TUNN_UNCLEAN=true + ;; + v) TUNN_VERBOSE="${TUNN_INVERT[$TUNN_VERBOSE]}" + ;; + h) usage 0 + ;; + esac +done + +# ensure list file exists +if [ ! -e "$TUNN_LISTFILE" ]; then + touch "$TUNN_LISTFILE" +fi + +# get args for operation (if any) +shift $(($OPTIND - 1)) + +# execute operation +$TUNN_OPFN "$@" From e3e884f8404ba33264a61ac2b5f88ee5ba0e5261 Mon Sep 17 00:00:00 2001 From: Kevin Pedro Date: Mon, 4 Aug 2025 20:22:48 -0500 Subject: [PATCH 2/3] shellcheck fixes --- tunn | 29 +++++++++++++++++------------ 1 file changed, 17 insertions(+), 12 deletions(-) diff --git a/tunn b/tunn index 5cfd54d..d269427 100755 --- a/tunn +++ b/tunn @@ -27,6 +27,7 @@ tunn_update() { # write open tunnels back into list for ((i=0; i < ${#TUNN_LIST[@]}; i++)); do tunn_entry $i + # shellcheck disable=SC2086 if ssh $TUNN_DEST -S "$TUNN_SOCKETNAME" -O check >&/dev/null; then echo "${TUNN_LIST[$i]}" >> "$TUNN_LISTFILE" else @@ -59,7 +60,7 @@ tunn_entry() { # operations tunn_make() { - TUNN_DEST="$@" + TUNN_DEST="$*" if [ -z "$TUNN_DEST" ]; then usage 1 fi @@ -69,7 +70,8 @@ tunn_make() { TUNN_SOCKETNAME="$TUNN_PREFIX"_"$TUNN_SOCKETNUM" # execute tunnel command - ssh $TUNN_DEST -N -D $TUNN_PORT -f -M -S "$TUNN_SOCKETNAME" 2>/dev/null + # shellcheck disable=SC2086 + ssh $TUNN_DEST -N -D "$TUNN_PORT" -f -M -S "$TUNN_SOCKETNAME" 2>/dev/null TUNN_EXIT=$? # add to list of tunnels @@ -105,14 +107,15 @@ tunn_kill() { # get this tunnel tunn_read - if [ $INDEX -lt ${#TUNN_LIST[@]} ]; then - tunn_entry $INDEX + if [ "$INDEX" -lt ${#TUNN_LIST[@]} ]; then + tunn_entry "$INDEX" else echo "Tunnel index $INDEX not found" exit 2 fi # execute kill command + # shellcheck disable=SC2086 ssh $TUNN_DEST -S "$TUNN_SOCKETNAME" -O exit tunn_echo "Killed tunnel: ${TUNN_LIST[$INDEX]}" @@ -122,21 +125,21 @@ tunn_kill() { # defaults TUNN_LISTFILE=~/.tunnlist -: ${TUNN_CONFIG:=~/.tunnconfig} +: "${TUNN_CONFIG:=~/.tunnconfig}" TUNN_SOCKETNAME="" TUNN_DEST="" -TUNN_INDEX="" TUNN_UNCLEAN="" # get config defaults if [ -e "$TUNN_CONFIG" ]; then + # shellcheck source=/dev/null source "$TUNN_CONFIG" fi -: ${TUNN_PREFIX:=~/.tsock} +: "${TUNN_PREFIX:=~/.tsock}" UTMP=$(id -u) UTMP=${UTMP:0-3} -: ${TUNN_PORT:=8${UTMP}} -: ${TUNN_VERBOSE:=false} +: "${TUNN_PORT:=8${UTMP}}" +: "${TUNN_VERBOSE:=false}" declare -A TUNN_INVERT TUNN_INVERT[true]=false @@ -171,11 +174,11 @@ usage() { $ECHO "-u \t (unclean) do not auto-remove closed tunnels from list" $ECHO "-v \t toggle verbosity (default: $TUNN_VERBOSE)" $ECHO "-h \t print this message and exit" - exit $1 + exit "$1" } # get operation -TUNN_OP=$1 +TUNN_OP="$1" shift 1 TUNN_OPFN="" @@ -202,6 +205,8 @@ while getopts "n:p:uvh" opt; do ;; h) usage 0 ;; + *) usage 1 + ;; esac done @@ -211,7 +216,7 @@ if [ ! -e "$TUNN_LISTFILE" ]; then fi # get args for operation (if any) -shift $(($OPTIND - 1)) +shift $((OPTIND - 1)) # execute operation $TUNN_OPFN "$@" From 8fba1c7c47ce7c25f0c366c780064c0eb4d75474 Mon Sep 17 00:00:00 2001 From: Kevin Pedro Date: Mon, 4 Aug 2025 20:31:41 -0500 Subject: [PATCH 3/3] add table of contents --- README.md | 25 +++++++++++++++++++++++++ 1 file changed, 25 insertions(+) diff --git a/README.md b/README.md index a863e49..8b627a5 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,31 @@ # lpc-scripts scripts of use on the cmslpc cluster +Table of Contents +================= + +* [call_host.sh](#call_hostsh) + * [Note](#note) + * [Usage](#usage) + * [Manual](#manual) + * [Automatic](#automatic) + * [Details](#details) + * [Options](#options) + * [Caveats](#caveats) +* [bind_condor.sh](#bind_condorsh) + * [Usage](#usage-1) + * [Setting up bindings](#setting-up-bindings) +* [tunn](#tunn) + * [Detailed usage](#detailed-usage) + * [Web browser usage](#web-browser-usage) +* [Unit and Integration testing](#unit-and-integration-testing) + * [Automated](#automated) + * [Manual](#manual-1) + * [Bats for Bash scripts](#bats-for-bash-scripts) + * [Pytest for Python modules](#pytest-for-python-modules) + + + ## `call_host.sh` Many commands are installed on interactive nodes but are not accessible inside containers.