Last active
September 19, 2025 15:37
-
-
Save ergosteur/3dc0d60ce3403c3c53ec47867f107cfa to your computer and use it in GitHub Desktop.
Basic ssh protocol handler for Windows or Linux. No external dependencies other than OpenSSH, optional Zenity for graphical dialog on Linux. Place somewhere on your local drive. Run once as admin to register the protocol handler in the registry (xdg on Linux). Enables clicking ssh:// links in browser, and in the Run dialog.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| @echo off | |
| SETLOCAL ENABLEDELAYEDEXPANSION | |
| REM ============================================================================ | |
| REM == Simple SSH Link Protocol Handler for Windows == | |
| REM == Uses the built-in Windows OpenSSH client. == | |
| REM == Includes self-installing registry setup. == | |
| REM == To install, right-click and "Run as administrator". == | |
| REM == == | |
| REM == Public Domain. https://tcpip.wtf (Modified from original) == | |
| REM ============================================================================ | |
| REM --- SCRIPT MODE DETECTION --- | |
| REM If no arguments are passed, switch to setup mode. | |
| IF "%~1"=="" GOTO :SETUP_MODE | |
| REM If arguments are passed, switch to connection/handler mode. | |
| GOTO :HANDLER_MODE | |
| REM ============================================================================ | |
| REM == SETUP MODE == | |
| REM ============================================================================ | |
| :SETUP_MODE | |
| echo. | |
| echo [ SETUP MODE ] | |
| echo This script will register itself as a system-wide handler for ssh:// URLs. | |
| echo It will use the Windows built-in OpenSSH client (ssh.exe). | |
| echo. | |
| REM --- 1. ADMINISTRATOR CHECK --- | |
| net session >nul 2>&1 | |
| IF %errorlevel% NEQ 0 ( | |
| echo ERROR: Administrator privileges are required for setup. | |
| echo Please right-click this script and select 'Run as administrator'. | |
| echo. | |
| pause | |
| GOTO :EOF | |
| ) | |
| echo [+] Administrator privileges confirmed. | |
| REM --- 2. FIND ssh.exe --- | |
| SET "SshExePath=%SystemRoot%\System32\OpenSSH\ssh.exe" | |
| IF NOT EXIST "!SshExePath!" ( | |
| echo. | |
| echo ERROR: Windows OpenSSH client could not be found at: | |
| echo !SshExePath! | |
| echo Please ensure the "OpenSSH Client" optional feature is installed. | |
| echo. | |
| pause | |
| GOTO :EOF | |
| ) | |
| echo [+] Found ssh.exe at: !SshExePath! | |
| REM --- 3. GET SCRIPT'S OWN PATH --- | |
| SET "ScriptPath=%~f0" | |
| echo [+] This script's path is: !ScriptPath! | |
| echo. | |
| REM --- 4. USER CONFIRMATION --- | |
| echo This will add the following registry entries: | |
| echo - Associate ssh:// protocol with a custom handler. | |
| echo - Set this script (!ScriptPath!) as the command to run. | |
| echo - Set the application icon to the Windows ssh.exe icon. | |
| echo. | |
| SET /P "CHOICE=Do you want to apply these registry changes? (Y/N): " | |
| IF /I NOT "%CHOICE%"=="Y" ( | |
| echo Setup cancelled by user. | |
| GOTO :EOF | |
| ) | |
| REM --- 5. APPLY REGISTRY CHANGES --- | |
| echo. | |
| echo Applying registry changes... | |
| REG ADD "HKCR\ssh" /ve /t REG_SZ /d "URL: ssh Protocol" /f >nul | |
| REG ADD "HKCR\ssh" /v "URL Protocol" /t REG_SZ /d "" /f >nul | |
| REG ADD "HKCR\ssh_custom_handler\shell\open\command" /ve /t REG_SZ /d "\"!ScriptPath!\" \"%%1\"" /f >nul | |
| REG ADD "HKLM\SOFTWARE\RegisteredApplications" /v "OpenSSH URL Handler" /t REG_SZ /d "Software\Classes\ssh_custom_handler\Capabilities" /f >nul | |
| REG ADD "HKCR\ssh_custom_handler\Capabilities\UrlAssociations" /v "ssh" /t REG_SZ /d "ssh_custom_handler" /f >nul | |
| REG ADD "HKCR\ssh_custom_handler\Application" /v "ApplicationIcon" /t REG_SZ /d "\"!SshExePath!\"" /f >nul | |
| REG ADD "HKCR\ssh_custom_handler\Application" /v "ApplicationName" /t REG_SZ /d "OpenSSH URL Handler" /f >nul | |
| REG ADD "HKCR\ssh_custom_handler\Application" /v "ApplicationDescription" /d "Handles ssh:// links via Windows OpenSSH" /f >nul | |
| REG ADD "HKCR\ssh_custom_handler\Application" /v "ApplicationCompany" /t REG_SZ /d "Microsoft" /f >nul | |
| echo. | |
| echo [+] SUCCESS: Registry values have been added. | |
| echo Your system is now configured to open ssh:// links with this script. | |
| echo. | |
| pause | |
| GOTO :EOF | |
| REM ============================================================================ | |
| REM == HANDLER MODE == | |
| REM ============================================================================ | |
| :HANDLER_MODE | |
| rem // Clear previous variables to be safe | |
| SET "SshUser=" | |
| SET "SshHost=" | |
| SET "FinalTarget=" | |
| SET "LegacyOpts=" | |
| rem // removing ssh:// from the input argument (%1) | |
| SET "TARGETHOST=%~1" | |
| SET "T2=!TARGETHOST:~6!" | |
| rem // If the hostname ends with a slash, remove it. | |
| IF "!T2:~-1!"=="/" SET "T2=!T2:~0,-1!" | |
| rem // Parse the target string into user and host | |
| echo !T2! | find "@" >nul | |
| IF !errorlevel! EQU 0 ( | |
| rem // Found '@', so URL is in user@host format | |
| for /f "tokens=1,2 delims=@" %%a in ("!T2!") do ( | |
| SET "SshUser=%%a" | |
| SET "SshHost=%%b" | |
| ) | |
| ) ELSE ( | |
| rem // No '@' found, so use the current Windows username as the default | |
| SET "SshUser=%USERNAME%" | |
| SET "SshHost=!T2!" | |
| ) | |
| rem // Prompt user to modify the username | |
| cls | |
| echo. | |
| echo Host: !SshHost! | |
| echo User: !SshUser! | |
| echo. | |
| SET /P "CHOICE=Change username? (y/N): " | |
| IF /I "%CHOICE%"=="Y" ( | |
| echo. | |
| SET /P "NewUser=Enter new username: " | |
| rem // Only change the username if the user actually entered something | |
| IF DEFINED NewUser SET "SshUser=!NewUser!" | |
| ) | |
| REM --- ADDED SECTION FOR LEGACY MODE --- | |
| echo. | |
| SET /P "LEGACY_CHOICE=Enable legacy mode for old devices? (y/N): " | |
| IF /I "!LEGACY_CHOICE!"=="Y" ( | |
| SET "LegacyOpts=-o KexAlgorithms=+diffie-hellman-group1-sha1,diffie-hellman-group14-sha1 -o HostKeyAlgorithms=+ssh-rsa -o MACs=+hmac-sha1,hmac-sha1-96" | |
| echo. | |
| echo [!] Legacy mode enabled. Insecure algorithms will be offered. | |
| ) | |
| REM --- END ADDED SECTION --- | |
| rem // Reconstruct the final connection string | |
| SET "FinalTarget=!SshUser!@!SshHost!" | |
| rem // Execute the connection | |
| echo. | |
| echo Connecting to: !FinalTarget! | |
| rem --- EXECUTION LINE --- | |
| start "SSH to !FinalTarget!" ssh.exe -A -C !LegacyOpts! !FinalTarget! | |
| GOTO :EOF |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| #!/bin/bash | |
| # ============================================================================ | |
| # == Simple SSH Link Protocol Handler for Linux == | |
| # == Uses XDG standards and works with most desktop environments. == | |
| # == == | |
| # == To install, run this script with sudo: == | |
| # == chmod +x ./ssh-handler.sh == | |
| # == sudo ./ssh-handler.sh --install == | |
| # == == | |
| # == To uninstall: == | |
| # == sudo ./ssh-handler.sh --uninstall == | |
| # == == | |
| # == To debug configuration: == | |
| # == ./ssh-handler.sh --debug == | |
| # == == | |
| # == Public Domain. https://tcpip.wtf == | |
| # ============================================================================ | |
| # --- SCRIPT MODE DETECTION --- | |
| if [ "$1" == "--install" ]; then | |
| MODE="SETUP" | |
| elif [ "$1" == "--uninstall" ]; then | |
| MODE="UNINSTALL" | |
| elif [ "$1" == "--debug" ]; then | |
| MODE="DEBUG" | |
| elif [[ "$1" == ssh://* ]]; then | |
| MODE="HANDLER" | |
| else | |
| echo "Usage:" | |
| echo " To install: $0 --install" | |
| echo " To uninstall: $0 --uninstall" | |
| echo " To debug: $0 --debug" | |
| echo " (This script is usually called by your desktop environment to handle a URL.)" | |
| exit 1 | |
| fi | |
| # A function to display errors both on stderr and via desktop notifications. | |
| show_error() { | |
| local message="$1" | |
| # Print to standard error | |
| echo "ERROR: $message" >&2 | |
| # Send a desktop notification if possible | |
| if command -v notify-send &> /dev/null; then | |
| notify-send --icon=dialog-error "SSH Handler Error" "$message" | |
| fi | |
| } | |
| # A function to display informational messages both on stdout and via notifications. | |
| show_info() { | |
| local message="$1" | |
| # Print to standard output | |
| echo "$message" | |
| # Send a desktop notification if possible | |
| if command -v notify-send &> /dev/null; then | |
| notify-send --icon=info "SSH Handler" "$message" | |
| fi | |
| } | |
| # ============================================================================ | |
| # == DEBUG MODE == | |
| # ============================================================================ | |
| if [ "$MODE" == "DEBUG" ]; then | |
| echo | |
| echo "[ DEBUG MODE ]" | |
| echo "Checking system configuration for ssh:// URL handling..." | |
| echo "--------------------------------------------------------" | |
| # 1. Check what the system thinks is the default handler | |
| echo "[1] Querying XDG for the default ssh:// handler..." | |
| DEFAULT_HANDLER=$(xdg-mime query default x-scheme-handler/ssh) | |
| if [ -n "$DEFAULT_HANDLER" ]; then | |
| echo " -> Default handler is: $DEFAULT_HANDLER" | |
| if [ "$DEFAULT_HANDLER" != "ssh-handler.desktop" ]; then | |
| echo " [!] WARNING: The default handler is not our script. Another application has taken precedence." | |
| echo " You may need to set the default handler manually in your desktop environment's settings," | |
| echo " or re-run 'sudo ./ssh-handler.sh --install' to try and set it again." | |
| else | |
| echo " [+] CORRECT: The default handler is correctly set to our script." | |
| fi | |
| else | |
| echo " [!] ERROR: No default handler is configured for ssh:// links on your system." | |
| echo " Please run 'sudo ./ssh-handler.sh --install'." | |
| fi | |
| # 2. Check if the desktop file exists and is correct | |
| echo | |
| echo "[2] Checking the .desktop file..." | |
| DESKTOP_FILE_PATH="/usr/share/applications/ssh-handler.desktop" | |
| if [ -f "$DESKTOP_FILE_PATH" ]; then | |
| echo " [+] FOUND: $DESKTOP_FILE_PATH" | |
| EXEC_LINE=$(grep "^Exec=" "$DESKTOP_FILE_PATH") | |
| echo " -> Exec line is: $EXEC_LINE" | |
| if [[ "$EXEC_LINE" != "Exec=/usr/local/bin/ssh-handler %u" ]]; then | |
| echo " [!] WARNING: The Exec line seems incorrect. It should be 'Exec=/usr/local/bin/ssh-handler %u'." | |
| fi | |
| else | |
| echo " [!] ERROR: The desktop file does not exist. The script is not installed correctly." | |
| fi | |
| # 3. Check if the script itself is installed | |
| echo | |
| echo "[3] Checking if the handler script is installed..." | |
| INSTALL_PATH="/usr/local/bin/ssh-handler" | |
| if [ -x "$INSTALL_PATH" ]; then | |
| echo " [+] FOUND and executable: $INSTALL_PATH" | |
| else | |
| echo " [!] ERROR: The script is not found or is not executable at $INSTALL_PATH." | |
| fi | |
| echo "--------------------------------------------------------" | |
| echo "Debug finished." | |
| echo | |
| exit 0 | |
| fi | |
| # ============================================================================ | |
| # == SETUP / UNINSTALL MODE == | |
| # ============================================================================ | |
| if [ "$MODE" == "SETUP" ] || [ "$MODE" == "UNINSTALL" ]; then | |
| # --- 1. SCRIPT AND DESKTOP FILE CONFIGURATION --- | |
| INSTALL_PATH="/usr/local/bin/ssh-handler" | |
| DESKTOP_FILE_NAME="ssh-handler.desktop" | |
| DESKTOP_FILE_PATH="/usr/share/applications/$DESKTOP_FILE_NAME" | |
| # --- 2. CHECK FOR ROOT PRIVILEGES --- | |
| if [ "$(id -u)" -ne 0 ]; then | |
| show_error "This operation requires root privileges. Please run with 'sudo'." | |
| exit 1 | |
| fi | |
| echo "[+] Root privileges confirmed." | |
| # --- 3. UNINSTALL LOGIC --- | |
| if [ "$MODE" == "UNINSTALL" ]; then | |
| echo | |
| show_info "Uninstalling SSH protocol handler..." | |
| if [ -f "$INSTALL_PATH" ]; then | |
| echo "[-] Removing script: $INSTALL_PATH" | |
| rm -f "$INSTALL_PATH" | |
| else | |
| echo "[!] Script not found at $INSTALL_PATH (already removed?)." | |
| fi | |
| if [ -f "$DESKTOP_FILE_PATH" ]; then | |
| echo "[-] Removing .desktop file: $DESKTOP_FILE_PATH" | |
| rm -f "$DESKTOP_FILE_PATH" | |
| update-desktop-database /usr/share/applications | |
| else | |
| echo "[!] Desktop file not found at $DESKTOP_FILE_PATH (already removed?)." | |
| fi | |
| show_info "Uninstallation complete." | |
| exit 0 | |
| fi | |
| # --- 4. SETUP LOGIC --- | |
| echo | |
| show_info "Installing SSH protocol handler..." | |
| echo "[+] Installing script to $INSTALL_PATH..." | |
| cp "$0" "$INSTALL_PATH" | |
| chmod +x "$INSTALL_PATH" | |
| echo "[+] Creating .desktop file at $DESKTOP_FILE_PATH..." | |
| cat > "$DESKTOP_FILE_PATH" << EOF | |
| [Desktop Entry] | |
| Name=SSH Protocol Handler | |
| Comment=Handles ssh:// links via the system's default terminal | |
| Exec=$INSTALL_PATH %u | |
| Icon=utilities-terminal | |
| Terminal=false | |
| Type=Application | |
| MimeType=x-scheme-handler/ssh; | |
| Categories=Network; | |
| EOF | |
| echo "[+] Registering the new handler with the system..." | |
| xdg-mime default "$DESKTOP_FILE_NAME" x-scheme-handler/ssh | |
| update-desktop-database /usr/share/applications | |
| echo | |
| show_info "SUCCESS: Your system is now configured to open ssh:// links." | |
| echo | |
| exit 0 | |
| fi | |
| # ============================================================================ | |
| # == HANDLER MODE == | |
| # ============================================================================ | |
| if [ "$MODE" == "HANDLER" ]; then | |
| # --- 1. PARSE THE URL --- | |
| FULL_URL="$1" | |
| TARGET="${FULL_URL#ssh://}" | |
| if [ "${TARGET: -1}" == "/" ]; then | |
| TARGET="${TARGET::-1}" | |
| fi | |
| if [[ "$TARGET" =~ "@" ]]; then | |
| SshUser="${TARGET%@*}" | |
| SshHost="${TARGET#*@}" | |
| else | |
| SshUser="$USER" | |
| SshHost="$TARGET" | |
| fi | |
| # Show initial notification that the link was received | |
| show_info "Received SSH link for ${SshUser}@${SshHost}..." | |
| # --- 2. DETERMINE DIALOG TOOL --- | |
| DIALOG_TOOL="read" | |
| if command -v zenity &> /dev/null; then | |
| DIALOG_TOOL="zenity" | |
| fi | |
| # --- 3. INTERACTIVE PROMPTS --- | |
| LegacyOpts="" | |
| USER_CANCELLED=false | |
| if [ "$DIALOG_TOOL" == "zenity" ]; then | |
| NewUser=$(zenity --entry --title="SSH Connection" --text="Connecting to Host: $SshHost\nEnter username:" --entry-text="$SshUser" --ok-label="Next") | |
| if [ $? -ne 0 ]; then | |
| USER_CANCELLED=true | |
| else | |
| if [ -n "$NewUser" ]; then SshUser="$NewUser"; fi | |
| zenity --question --title="Legacy Mode" --text="Enable legacy mode for old devices?\n(This uses insecure algorithms)" --ok-label="Yes, Enable" --cancel-label="No" | |
| if [ $? -eq 0 ]; then | |
| LegacyOpts="-o KexAlgorithms=+diffie-hellman-group1-sha1,diffie-hellman-group14-sha1 -o HostKeyAlgorithms=+ssh-rsa -o MACs=+hmac-sha1,hmac-sha1-96" | |
| fi | |
| fi | |
| else | |
| TUI_SCRIPT=$(printf 'SshUser="%s"; SshHost="%s"; clear; echo; echo " Host: $SshHost"; echo " User: $SshUser"; echo; read -p "Change username? (y/N): " choice; choice=${choice,,}; if [[ "$choice" == "y" || "$choice" == "yes" ]]; then read -p "Enter new username: " NewUser; if [ -n "$NewUser" ]; then SshUser="$NewUser"; fi; fi; echo; read -p "Enable legacy mode? (y/N): " legacy_choice; legacy_choice=${legacy_choice,,}; if [[ "$legacy_choice" == "y" || "$legacy_choice" == "yes" ]]; then LegacyOpts="-o KexAlgorithms=+diffie-hellman-group1-sha1,diffie-hellman-group14-sha1 -o HostKeyAlgorithms=+ssh-rsa -o MACs=+hmac-sha1,hmac-sha1-96"; fi; FINAL_TARGET="$SshUser@$SshHost"; echo; echo "Connecting to: $FINAL_TARGET"; echo "-------------------------------------------"; SSH_ARGS_TUI=(-A -C); if [ -n "$LegacyOpts" ]; then read -ra LEGACY_ARRAY_TUI <<< "$LegacyOpts"; SSH_ARGS_TUI+=("${LEGACY_ARRAY_TUI[@]}"); fi; SSH_ARGS_TUI+=("$FINAL_TARGET"); exec ssh "${SSH_ARGS_TUI[@]}"' "$SshUser" "$SshHost") | |
| # Find and use a terminal emulator | |
| TERMINAL_CMD=() | |
| TERMINAL_NAME="" | |
| if command -v x-terminal-emulator &> /dev/null && [ -x "$(realpath /usr/bin/x-terminal-emulator 2>/dev/null)" ]; then | |
| TERMINAL_NAME="x-terminal-emulator" | |
| TERMINAL_CMD=(x-terminal-emulator -T "SSH to $SshHost" -e "bash -c '$TUI_SCRIPT'") | |
| elif command -v gnome-terminal &> /dev/null; then | |
| TERMINAL_NAME="gnome-terminal" | |
| TERMINAL_CMD=(gnome-terminal --title "SSH to $SshHost" -- bash -c "$TUI_SCRIPT") | |
| elif command -v xfce4-terminal &> /dev/null; then | |
| TERMINAL_NAME="xfce4-terminal" | |
| TERMINAL_CMD=(xfce4-terminal -T "SSH to $SshHost" -e "bash -c '$TUI_SCRIPT'") | |
| fi | |
| if [ -n "$TERMINAL_NAME" ]; then | |
| show_info "Launching terminal ($TERMINAL_NAME) for interactive prompt..." | |
| "${TERMINAL_CMD[@]}" & | |
| else | |
| show_error "Could not find a known terminal emulator to run interactive prompts." | |
| exit 1 | |
| fi | |
| exit 0 | |
| fi | |
| if $USER_CANCELLED; then | |
| show_info "SSH connection cancelled by user." | |
| exit 0 | |
| fi | |
| # --- 4. BUILD AND LAUNCH --- | |
| FINAL_TARGET="$SshUser@$SshHost" | |
| SSH_ARGS=(-A -C) | |
| if [ -n "$LegacyOpts" ]; then | |
| read -ra LEGACY_ARRAY <<< "$LegacyOpts" | |
| SSH_ARGS+=("${LEGACY_ARRAY[@]}") | |
| fi | |
| SSH_ARGS+=("$FINAL_TARGET") | |
| # Find and use a terminal emulator to launch the final ssh command | |
| LAUNCH_CMD=() | |
| TERMINAL_NAME="" | |
| if command -v x-terminal-emulator &> /dev/null && [ -x "$(realpath /usr/bin/x-terminal-emulator 2>/dev/null)" ]; then | |
| TERMINAL_NAME="x-terminal-emulator" | |
| LAUNCH_CMD=(x-terminal-emulator -T "SSH to $FINAL_TARGET" -e "ssh ${SSH_ARGS[*]}") | |
| elif command -v gnome-terminal &> /dev/null; then | |
| TERMINAL_NAME="gnome-terminal" | |
| LAUNCH_CMD=(gnome-terminal --title "SSH to $FINAL_TARGET" -- ssh "${SSH_ARGS[@]}") | |
| elif command -v xfce4-terminal &> /dev/null; then | |
| TERMINAL_NAME="xfce4-terminal" | |
| LAUNCH_CMD=(xfce4-terminal --title "SSH to $FINAL_TARGET" -x ssh "${SSH_ARGS[@]}") | |
| elif command -v xterm &> /dev/null; then | |
| TERMINAL_NAME="xterm" | |
| LAUNCH_CMD=(xterm -T "SSH to $FINAL_TARGET" -e "ssh ${SSH_ARGS[*]}") | |
| fi | |
| if [ -n "$TERMINAL_NAME" ]; then | |
| show_info "Connecting to $SshHost via $TERMINAL_NAME..." | |
| "${LAUNCH_CMD[@]}" & | |
| else | |
| show_error "Could not find a known terminal emulator to launch the SSH session." | |
| exit 1 | |
| fi | |
| exit 0 | |
| fi | |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment