#!/bin/sh set -eu VERSION="0.0.1-20250509" PROXIFIER_DIR="${HOME}/.freeleaps/proxifier" help() { echo "Freeleaps Cluster Proxifier (Version: ${VERSION})" echo "" echo "This script helps you to forward Kubernetes service ports to your local machine." echo "It maintains the forwarding state and provides commands to manage port forwarding." echo "" echo "Usage: freeleaps-cluster-proxifier " echo "" echo "Sub Commands:" echo " forward,-f,--forward / -p : Forward a service port to local" echo " stop,-s,--stop / Stop forwarding a service" echo " list,-l,--list List all forwarded services" echo " list-available,-la,--list-available List all available services" echo " help,-h,--help Show this help message" } ensure_proxifier_dir() { if [ ! -d "${PROXIFIER_DIR}" ]; then mkdir -p "${PROXIFIER_DIR}" fi } get_process_file() { namespace="$1" service="$2" echo "${PROXIFIER_DIR}/${namespace}-${service}.pid" } forward_port() { if [ $# -lt 1 ]; then echo "[ERROR] Invalid number of arguments for forward command" echo "[TIP] Usage: freeleaps-cluster-proxifier forward / -p :" exit 1 fi # Parse namespace/service IFS='/' read -r namespace service <:" exit 1 fi ports="$3" # Validate service exists and user has permissions if ! kubectl get svc "${service}" -n "${namespace}" >/dev/null 2>&1; then if kubectl get namespace "${namespace}" >/dev/null 2>&1; then echo "[ERROR] Either the service '${service}' doesn't exist in namespace '${namespace}' or you don't have permission to access it" echo "[TIP] Please contact your cluster administrator to request access to this service" else echo "[ERROR] Namespace '${namespace}' doesn't exist or you don't have permission to access it" echo "[TIP] Please contact your cluster administrator to request access to this namespace" fi exit 1 fi process_file=$(get_process_file "${namespace}" "${service}") if [ -f "${process_file}" ]; then echo "[ERROR] Service ${service} in namespace ${namespace} is already being forwarded" echo "[TIP] Use 'freeleaps-cluster-proxifier list' to see active forwards" exit 1 fi ensure_proxifier_dir echo "[FORWARD] Starting port forward for ${service} in namespace ${namespace}..." kubectl port-forward -n "${namespace}" "svc/${service}" "${ports}" > /dev/null 2>&1 & pid=$! # Store PID and port mapping echo "${pid}:${ports}" > "${process_file}" echo "[FORWARD] Port forward started successfully" echo "[INFO] Service ${service}.${namespace} is now mapping with ${ports}" } stop_forward() { if [ $# -ne 1 ]; then echo "[ERROR] Invalid number of arguments for stop command" echo "[TIP] Usage: freeleaps-cluster-proxifier stop /" exit 1 fi # Parse namespace/service IFS='/' read -r namespace service </dev/null 2>&1; then rm "${process_file}" echo "[STOP] Stopped forwarding service ${service} in namespace ${namespace}" else echo "[WARNING] Process not found, cleaning up state file" rm "${process_file}" fi } list_forwards() { ensure_proxifier_dir echo "Belows are all active port forwards:" printf "%-30s %-60s %-15s %-10s\n" "Namespace" "Service" "Port Mapping" "PID" for file in "${PROXIFIER_DIR}"/*.pid; do if [ -f "${file}" ]; then name=$(basename "${file}" .pid) namespace=$(echo "${name}" | cut -d'-' -f1) service=$(echo "${name}" | cut -d'-' -f2-) data=$(cat "${file}") pid=$(echo "${data}" | cut -d: -f1) ports=$(echo "${data}" | cut -d: -f2-) # Check if process is still running if kill -0 "${pid}" >/dev/null 2>&1; then printf "%-30s %-60s %-15s %-10s\n" "${namespace}" "${service}" "${ports}" "${pid}" else echo "[WARNING] Cleaning up stale forward for ${service} in namespace ${namespace}" rm "${file}" fi fi done } list_available_services() { echo "Belows are all available services that you can forward:" printf "%-30s %-60s %-10s\n" "Namespace" "Service" "Ports" # Get all namespaces user has access to kubectl get namespaces -o name | cut -d'/' -f2 | while read -r ns; do # Get services in each namespace if kubectl auth can-i get services -n "${ns}" >/dev/null 2>&1; then kubectl get services -n "${ns}" \ --no-headers \ -o custom-columns="Namespace:.metadata.namespace,Service:.metadata.name,Ports:.spec.ports[*].port" | \ while read -r line; do # Only show if user has permission to port-forward svc_name=$(echo "${line}" | awk '{print $2}') if kubectl auth can-i get services/"${svc_name}" -n "${ns}" >/dev/null 2>&1; then namespace=$(echo "${line}" | awk '{print $1}') service=$(echo "${line}" | awk '{print $2}') ports=$(echo "${line}" | awk '{print $3}') printf "%-30s %-60s %-10s\n" "${namespace}" "${service}" "${ports}" fi done fi done } main() { if [ $# -lt 1 ]; then echo "[ERROR] No sub-command provided" echo "[TIP] Run 'freeleaps-cluster-proxifier -h' to see available sub-commands" exit 1 fi subcommand="$1" shift case "${subcommand}" in forward|-f|--forward) forward_port "$@" ;; stop|-s|--stop) stop_forward "$@" ;; list|-l|--list) list_forwards ;; list-available|-la|--list-available) list_available_services ;; help|-h|--help) help ;; *) echo "[ERROR] Invalid sub-command: ${subcommand}" help exit 1 ;; esac } main "$@"