Skip to content

Instantly share code, notes, and snippets.

@pyt0xic
Last active March 28, 2025 11:40
Show Gist options
  • Save pyt0xic/fa24cbfbb462e36e7d447d762ce95194 to your computer and use it in GitHub Desktop.
Save pyt0xic/fa24cbfbb462e36e7d447d762ce95194 to your computer and use it in GitHub Desktop.
This script creates a tunnel to a PostgreSQL database using socat from within a kube pod
#!/usr/bin/env bash
# This script creates a tunnel to a PostgreSQL database using socat from within a kube pod.
# Will download socat binary if not found in the system.
# Configuration variables
REMOTE_HOST="$DB_HOSTNAME"
LOCAL_PORT=5432
REMOTE_PORT=5432
SOCAT_PATH="$(command -v socat || echo '/tmp/socat')"
SOCAT_URL="https://github.com/3ndG4me/socat/releases/download/v1.7.3.3/socatx64.bin"
LONGOPTS="host:,local:,remote:,socat-path:,socat-url:,help"
OPTIONS="H:l:r:p:u:h"
set -o errexit -o pipefail -o noclobber -o nounset
# Function to display usage
print_usage() {
echo "Usage: $0 [OPTIONS]"
echo "Create a tunnel to a PostgreSQL database using socat."
echo
echo "Options:"
echo " -H, --host HOST Database hostname (required if DB_HOSTNAME not set)"
echo " -l, --local PORT Local port to listen on (default: 5432)"
echo " -r, --remote PORT Remote port to connect to (default: 5432)"
echo " -p, --socat-path PATH Path to socat binary (default: \$(command -v socat || echo '/tmp/socat'))"
echo " -u, --socat-url URL Download URL for socat (default: https://github.com/3ndG4me/socat/releases/download/v1.7.3.3/socatx64.bin)"
echo " -h, --help Display this help and exit"
echo
echo "Environment variables:"
echo " DB_HOSTNAME Database hostname (used if --host not specified)"
}
# Function for logging
log_message() {
echo "[$(date '+%Y-%m-%d %H:%M:%S')] $1"
}
cleanup() {
log_message "Cleaning up"
# Remove $SOCAT_PATH if it is a temporary file and exists
[ -f "$SOCAT_PATH" ] && [ "$SOCAT_PATH" = "/tmp/socat" ] && rm -f "$SOCAT_PATH"
}
# Error handling function
handle_error() {
log_message "ERROR: $1"
# Clean up if needed
cleanup
# print_usage
exit 1
}
# Parse args using new getopt from util-linux.
parse_args() {
# ignore errexit with `&& true`
getopt --test >/dev/null && true
if [[ $? -ne 4 ]]; then
die 'Cannot parse args, "getopt --test" failed in this environment.'
fi
# -temporarily store output to be able to check for errors
# -activate quoting/enhanced mode (e.g. by writing out “--options”)
# -pass arguments only via -- "$@" to separate them correctly
# -if getopt fails, it complains itself to stdout
PARSED=$(getopt --options=$OPTIONS --longoptions=$LONGOPTS --name "$0" -- "$@") || exit 2
# read getopt’s output this way to handle the quoting right:
eval set -- "$PARSED"
# Extract options and their arguments into variables.
while true; do
case "$1" in
-H | --host)
REMOTE_HOST="$2"
shift 2
;;
-l | --local)
LOCAL_PORT="$2"
shift 2
;;
-r | --remote)
REMOTE_PORT="$2"
shift 2
;;
-p | --socat-path)
SOCAT_PATH="$2"
shift 2
;;
-u | --socat-url)
SOCAT_URL="$2"
shift 2
;;
-h | --help)
print_usage
exit 0
;;
--)
shift
break
;;
*)
handle_error "Unknown option: $1"
;;
esac
done
}
download_socat() {
log_message "Downloading socat binary from $SOCAT_URL"
curl -L "$SOCAT_URL" -o "$SOCAT_PATH" || handle_error "Failed to download socat"
log_message "Making socat binary executable"
chmod +x "$SOCAT_PATH" || handle_error "Failed to make socat executable"
log_message "Using socat binary at $SOCAT_PATH"
}
ensure_socat_installed() {
if [ -x "$SOCAT_PATH" ]; then
log_message "Using socat binary at $SOCAT_PATH"
else
download_socat
fi
}
print_kubectl_port_forward_command() {
log_message "To forward the local port to a Kubernetes pod, run the following command:"
echo -e "\t\tkubectl port-forward $(hostname) 54320:$LOCAL_PORT\n"
}
print_psql_command() {
log_message "To connect to the database, run the following command:"
echo -e "\t\tPGPASSWORD=$DB_PASSWORD psql -h localhost -p 54320 -U $DB_USERNAME $DB_DATABASE\n"
}
create_tunnel() {
if [ -z "$REMOTE_HOST" ]; then
handle_error "Database hostname not specified"
fi
log_message "Creating tunnel from localhost:$LOCAL_PORT to $REMOTE_HOST:$REMOTE_PORT"
print_kubectl_port_forward_command
print_psql_command
trap 'echo "Tunnel closed"; [ "$SOCAT_PATH" = "/tmp/socat" ] && rm -f "$SOCAT_PATH"; exit 0' INT TERM
"$SOCAT_PATH" tcp-listen:"$LOCAL_PORT",fork,reuseaddr tcp-connect:"$REMOTE_HOST":"$REMOTE_PORT" || handle_error "Tunnel failed"
}
parse_args "$@"
ensure_socat_installed && create_tunnel
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment