Skip to content
Permalink

Comparing changes

Choose two branches to see what’s changed or to start a new pull request. If you need to, you can also or learn more about diff comparisons.

Open a pull request

Create a new pull request by comparing changes across two branches. If you need to, you can also . Learn more about diff comparisons here.
base repository: ledup/ssh
Failed to load repositories. Confirm that selected base ref is valid, then try again.
Loading
base: v1.0.0
Choose a base ref
...
head repository: ledup/ssh
Failed to load repositories. Confirm that selected head ref is valid, then try again.
Loading
compare: master
Choose a head ref
  • 10 commits
  • 3 files changed
  • 2 contributors

Commits on Apr 19, 2019

  1. Fix sshconfig file parsing to detect all host aliases

    - list: print one line per host aliases
    - list: add option --quiet to print only hosts aliases
    - in: new configuration key 'default.ssh' to avoid providing a server as argument
    - in: --server now handle hosts aliases
    bonidier committed Apr 19, 2019
    Copy the full SHA
    48d2951 View commit details

Commits on Apr 24, 2019

  1. Merge pull request #1 from ledup/manage-host-aliases

    Fix sshconfig file parsing to detect all host aliases
    dgaussin authored Apr 24, 2019
    Copy the full SHA
    b6ba8c7 View commit details

Commits on Nov 5, 2021

  1. rework sshconfig parsing to detect other useful keywords

    - ssh_list(): print port & user if provided. detect duplicate hosts, skip host with special characters
    - ssh_in(): print openssh client version
    - add ssh_get_sshconfig() to print path to cached sshconfig if exists
    - sshconfig cache build: avoid to load .led/sshconfig twice when called from user HOME
    bonidier committed Nov 5, 2021
    Copy the full SHA
    bf7fff3 View commit details
  2. Merge pull request #2 from ledup/sshcnfig_parser

    rework sshconfig parsing to detect other useful keywords
    bonidier authored Nov 5, 2021
    Copy the full SHA
    f4e043c View commit details

Commits on Nov 12, 2021

  1. add function ssh_exec()

    - wrapper to ssh binary which use sshconfig, print destination host and command to execute
    - add some default ssh options
    bonidier committed Nov 12, 2021
    Copy the full SHA
    08489b6 View commit details

Commits on Nov 18, 2021

  1. enhance output when list + fix ssh conection behavior

    - ssh list: print message if empty sshconfig
    - ssh in: remove ConnectTimeout option
    - drop fallback_deprecated_ssh()
    bonidier committed Nov 18, 2021
    Copy the full SHA
    2356389 View commit details

Commits on Nov 23, 2021

  1. only use .led/sshconfig from current directory

    - add ssh_scp(): simple wrapper to 'scp' which use sshconfig by default
    - cleanup help
    bonidier committed Nov 23, 2021
    Copy the full SHA
    7bc8566 View commit details
  2. Copy the full SHA
    05a0345 View commit details

Commits on Nov 24, 2021

  1. Copy the full SHA
    081ef3a View commit details

Commits on Dec 3, 2021

  1. Copy the full SHA
    ca49845 View commit details
Showing with 215 additions and 69 deletions.
  1. +1 −0 .gitignore
  2. +209 −66 .led/plugins/ssh.plugin.sh
  3. +5 −3 README.md
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
.led/sshconfig
275 changes: 209 additions & 66 deletions .led/plugins/ssh.plugin.sh
Original file line number Diff line number Diff line change
@@ -1,23 +1,23 @@
#!/usr/bin/env bash

SSH_PLUGIN_CACHE_CONFIG=${LED_CACHE_USER_DIR}/sshconfig
# not ready for current led version
#readonly SSHCONFIG_FILE=".led/sshconfig"
SSHCONFIG_FILE=".led/sshconfig"

# plugin

# Plugin usage:
# led ssh [command] [-h|--help]
#
# Commands :
# Commands:
# in Open console on server
# list List available servers
#
# If no command is provided, the command in is the default one.
# If no command is provided, the command 'in' is the default one.
#
# @autocomplete ssh: in list --user --server
# @autocomplete ssh --server: [led ssh list | awk '{print $1}']
# @autocomplete ssh --user: dev root
# @autocomplete ssh -s: [led ssh list | awk '{print $1}']
# @autocomplete ssh -u: dev root
# @autocomplete ssh --server: [led ssh list -q]
# @autocomplete ssh -s: [led ssh list -q]
ssh_plugin() {
local command=$1

@@ -32,53 +32,153 @@ ssh_plugin() {
# Usage: led ssh list
#
# List configured servers with SSH access
# Options
# -q, --quiet Only list hosts aliases
#
# @autocomplete ssh list: --quiet
ssh_list() {
ssh_do_cache

local host
local hostname
local host_displayed=()
local line
local quiet

# shellcheck disable=SC2046
set -- $(_lib_utils_get_options "q" "quiet" "$@")

# if quiet mode is enabled, will only print host aliases from ssh config file
while [ -n "$#" ]; do
case $1 in
--quiet | -q)
quiet="quiet"
shift
;;
--)
shift
break
;;
esac
done

if [[ ! -f "${SSHCONFIG_FILE}" ]]; then
[[ -z "${quiet}" ]] && echo "${SSHCONFIG_FILE} file not found" >&2
return 1
elif [[ ! -s "${SSHCONFIG_FILE}" ]]; then
[[ -z "${quiet}" ]] && echo "${SSHCONFIG_FILE} file is empty!" >&2
return 1
fi

local hostname host host_aliases port user
local str_hostinfo
local key value

# tips inspired by http://www.commandlinefu.com/commands/view/13419/extract-shortcuts-and-hostnames-from-.sshconfig
local sshconfig_data
# generate list of sshconfig of some keywords with their value. One line by 'Host' keyword found
# handled keyword: Host, Hostname, Port, User
#
# we order output only after duplicate host exclusion
sort < <(
while read -r host hostname; do
host_in_list=$(
echo "${host_displayed[@]}" | tr ' ' '\n' | grep -q "^${host}$"
echo "$?"
)
[ "$host_in_list" -eq 0 ] && continue
print_padded "$host" "$hostname"
host_displayed+=("$host")
done < <(awk 'BEGIN {IGNORECASE = 1} $1=="Host"{$1="";H=substr($0,2)};$1=="HostName"{print H,$2}' "${SSH_PLUGIN_CACHE_CONFIG}"))
# sample output format:
# Host=host1 host1_alias | Hostname=foo1.tld | Port=2222 |
# Host=host2 | Hostname=foo2.tld | User=jdoe
sshconfig_data=$(awk 'BEGIN {IGNORECASE = 1; flag = 1 }
flag {
if ($1 == "Host") { $1=""; H=substr($0,2); data=""; $1="Host" }
if ($1 ~ /^(Host|Hostname|Port|User)$/) {
key=$1; $1=""; value=substr($0,2);
data = data key "=" value " | "
}
config[H]=data
}
/Host /{ flag=1; }
END { for (key in config) { printf "%s\n", config[key] } }' \
"${SSHCONFIG_FILE}" | sort)

# raw data, for debugging
#echo "${sshconfig_data}"

local duplicated_hosts=()
local host_extras
while read -r line; do
host_aliases=
hostname=
port=
user=
while IFS='=' read -r -d '|' key value; do
# trim spaces
read -r key <<<"$key"
read -r value <<<"$value"
# escape non-printable character with blackslash
value=$(printf "%q" "${value}")

# search for key in lowercase
case ${key,,} in
host) host_aliases=$value ;;
hostname) hostname=$value ;;
port) port=$value ;;
user) user=$value ;;
esac
done <<<"${line}"

for host in ${host_aliases}; do
host_extras=()
## check host alias
# A host alias from a Host line with many aliases has a blackslash at the end
[[ "${host: -1}" == "\\" ]] && host=${host::-1}

if ! [[ $host =~ ^[a-zA-Z0-9._-]+$ ]]; then
# skip host with other characters (like wildcard)
continue
fi

if in_array "${host}" "${host_displayed[@]}"; then
duplicated_hosts+=("$host")
continue
fi

host_displayed+=("${host}")
if [ -n "${quiet}" ]; then
echo "${host}"
else
[[ -n "${port}" ]] && host_extras+=("port: $port")
[[ -n "${user}" ]] && host_extras+=("user: $user")
str_hostinfo="${hostname}"
[[ ${#host_extras[*]} -ge 1 ]] && str_hostinfo+=" [${host_extras[*]}]"
print_padded "${host}" "${str_hostinfo}"
fi
done

done <<<"${sshconfig_data}"

if [[ ${#duplicated_hosts[*]} -ge 1 && -z "${quiet}" ]]; then
echo -e "\\n[SKIPPED] Host(s) in '${duplicated_hosts[*]}' duplicated! Please check your sshconfig files" >&2
fi
}

# in
# Usage: led ssh in [OPTIONS]
#
# Connect to servers using SSH.
# An sshconfig file must exist in the global or local directory .led
# An .led/sshconfig file must exist in current directory
#
# Options
# -s, --server Server name to use
# -u, --user Override user to use
#
# You can define options using configuration keys:
# default.ssh myhost
#
# @autocomplete ssh in: --user --server
# @autocomplete ssh in --server: [led ssh list | awk '{print $1}']
# @autocomplete ssh in --user: dev root
# @autocomplete ssh in -s: [led ssh list | awk '{print $1}']
# @autocomplete ssh in -u: dev root
# @autocomplete ssh in --server: [led ssh list -q]
# @autocomplete ssh in -s: [led ssh list -q]
ssh_in() {
local user
local server
local valid_ssh_host
local ssh_remote

local default_ssh_server
default_ssh_server="$(_config_get_value default.ssh)"

# shellcheck disable=SC2046
set -- $(_lib_utils_get_options "u:s:l" "user:,server:" "$@")
set -- $(_lib_utils_get_options "u:s:" "user:,server:" "$@")

while [ ! -z "$#" ]; do
while [ -n "$#" ]; do
case $1 in
-u | --user)
user=$2
@@ -93,67 +193,110 @@ ssh_in() {
break
;;
-*)
echo "bad option:$1"
echo "bad option: $1"
shift
break
;;
esac
done

if [ -z "$server" ]; then
fallback_deprecated_ssh "$user" "$server" "$@"
server=${server:-"${default_ssh_server}"}

if [ -z "${server}" ]; then
help ssh
exit
return 1
fi

ssh_do_cache
local ssh_version
ssh_version=$(command ssh -V 2>&1)
# only keep OpenSSH version
ssh_version=${ssh_version%,*}

local ssh_host_aliases
# only get host aliases
mapfile -t ssh_host_aliases < <(ssh_list --quiet)
if [[ ${#ssh_host_aliases[@]} -eq 0 ]]; then
echo "no host alias found"
return 1
fi

# test if the server is a Host entry defined in ssh config file
valid_ssh_host=$(
grep -i '^Host ' "${SSH_PLUGIN_CACHE_CONFIG}" | sed 's/Host //I' | grep -q "^${server}$"
echo $?
)
# if yes, connect to the remote server
if [ "${valid_ssh_host}" -eq 0 ]; then
if in_array "${server}" "${ssh_host_aliases[@]}"; then
# if yes, connect to the remote server
if [ -n "${user}" ]; then
ssh_remote="${user}@${server}"
else
ssh_remote="${server}"
fi
command ssh "${ssh_remote}" -F "${SSH_PLUGIN_CACHE_CONFIG}"
echo "[SSH client '${ssh_version}']"
ssh_exec "${ssh_remote}"
else
fallback_deprecated_ssh "$user" "$server" "$@"
echo "Can't find server named $server"
exit 1
echo -e "Can't find server named '${server}'\\n"
return 1
fi
}

# If ssh command is known as deprecated yet, fallback to docker exec
fallback_deprecated_ssh() {
if key_in_array "ssh" in DEPRECATED_COMMANDS; then
#
# ssh_exec <host> <command>
# if command is not set, run SSH interactivly
#
ssh_exec() {
local ssh_bin

echo "Warning: Deprecated command. Please use led in to get console on container or install ssh plugin to connect servers."
local server=$1
local command=$2

# get command from remaining arguments
local user=${1:-"dev"}
shift
local server=${1:-"apache"}
shift
local cmd=${*:-$cmd}
if [[ $# -eq 0 ]]; then
echo "ssh_exec <server> [<command>]" >&2
return 1
fi

_docker_exec "${user}" "${server}" "${cmd}"
local ssh_options=()
ssh_add_sshconfig_option ssh_options

ssh_options+=(-o"PreferredAuthentications=publickey")

ssh_bin=$(type -P ssh)
if [[ -n "${command}" ]]; then
ssh_options+=(-n)
echo -e ":: [host: '${server}'] Executing SSH command '${command}'" >&2
${ssh_bin} "${ssh_options[@]}" "${server}" -- "${command}"
else
echo -e ":: [host: '${server}'] Interactive connection" >&2
${ssh_bin} "${ssh_options[@]}" "${server}"
fi
return 0
}

# Generate single file with all ssh config files founds
ssh_do_cache() {
local sshconfig=(.led/sshconfig "${HOME}"/.led/sshconfig "${SCRIPT_DIR}"/etc/sshconfig)
# Simple wrapper to scp which handle .led/sshconfig file by default
#
# ssh_scp <source> <target>
ssh_scp() {
local scp_bin

cat /dev/null >"${SSH_PLUGIN_CACHE_CONFIG}"
for f in "${sshconfig[@]}"; do
[ -f "$f" ] && cat "$f" >>"${SSH_PLUGIN_CACHE_CONFIG}"
done
if [[ $# -lt 2 ]]; then
echo "ssh_scp <source> <target>" >&2
return 1
fi

return 0
local scp_options=()
ssh_add_sshconfig_option scp_options
scp_options+=(-o"PreferredAuthentications=publickey")

scp_bin=$(type -P scp)
echo -e ":: Executing SCP command '$*'" >&2
${scp_bin} "${scp_options[@]}" "$@"
}

# add ssh/scp option to a defined array if sshconfig file exists
#
# example:
# ssh_add_sshconfig_option array_name
# ssh "${array_name[@]}" ...
# scp "${array_name[@]}" ...
ssh_add_sshconfig_option() {
local ssh_opt
declare -n ssh_opt=$1
if [[ -f "${SSHCONFIG_FILE}" ]]; then
ssh_opt+=(-F "${SSHCONFIG_FILE}")
fi
}
8 changes: 5 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,11 +1,13 @@
# SSH plugin for led

This plugin adds commands to "led" to connect to servers using SSH.
This plugin adds commands to *led* to connect to servers using SSH.

Configuration file has to be stored in files named *sshconfig* in local (*.led*) or user directory (*~/.led*).
Configuration file has to be stored in a file named `.led/sshconfig` in your project's directory.

The files must be in the same format as the SSH client configuration files.

Install the plugin with

led plugin install ssh
```
led plugin add ssh
```