| 1 |
#!/usr/bin/env bash |
|
| 2 |
set -euo pipefail |
|
| 3 | ||
| 4 |
real_ssh="/usr/bin/ssh" |
|
| 5 |
ssh_config="$HOME/.ssh/config" |
|
| 6 |
remote_agent="/run/user/0/gnupg/S.gpg-agent.ssh" |
|
| 7 | ||
| 8 |
# Access model, May 2026 |
|
| 9 |
# ---------------------- |
|
| 10 |
# |
|
| 11 |
# Cheia fizica este montata pe is-jumper. Wrapper-ul local nu mai face bridge |
|
| 12 |
# de agent in /tmp; in schimb ruleaza clientul SSH activ pe is-jumper: |
|
| 13 |
# |
|
| 14 |
# local ssh wrapper |
|
| 15 |
# -> is-jumper (192.168.2.100, acces local cu id_ed25519) |
|
| 16 |
# -> J1/J2/j1/j2 (autentificare cu agentul fizic de pe is-jumper) |
|
| 17 |
# -> ssh final-host |
|
| 18 |
# |
|
| 19 |
# Custom jump flags (stripped by wrapper, not passed to real ssh): |
|
| 20 |
# -J1 use J1 via VPN (default for configured hosts) |
|
| 21 |
# -J2 use J2 via VPN |
|
| 22 |
# -j1 use j1 via public DNS (urgente, fara ruta VPN) |
|
| 23 |
# -j2 use j2 via public DNS (urgente, fara ruta VPN) |
|
| 24 |
# |
|
| 25 |
# Hosturi arbitrare (fara config SSH): wrapper-ul le ruteza prin jump doar daca |
|
| 26 |
# unul dintre flagurile de mai sus este prezent explicit. |
|
| 27 | ||
| 28 |
target_user="" |
|
| 29 |
target_host="" |
|
| 30 |
target_port="" |
|
| 31 |
target_auth="" |
|
| 32 |
found_target=0 |
|
| 33 |
cmd_args=() |
|
| 34 |
ssh_config_args=() |
|
| 35 |
jump_alias="j1" |
|
| 36 |
custom_jump_set=0 |
|
| 37 |
host_configured=1 |
|
| 38 |
target_is_jump=0 |
|
| 39 |
want_subsystem=0 |
|
| 40 |
user_option="" |
|
| 41 |
port_option="" |
|
| 42 | ||
| 43 |
has_explicit_config() {
|
|
| 44 |
local arg |
|
| 45 | ||
| 46 |
for arg in "$@"; do |
|
| 47 |
case "$arg" in |
|
| 48 |
-F|-F*) |
|
| 49 |
return 0 |
|
| 50 |
;; |
|
| 51 |
esac |
|
| 52 |
done |
|
| 53 | ||
| 54 |
return 1 |
|
| 55 |
} |
|
| 56 | ||
| 57 |
quote_cmd() {
|
|
| 58 |
local out="" q part |
|
| 59 | ||
| 60 |
for part in "$@"; do |
|
| 61 |
printf -v q "%q" "$part" |
|
| 62 |
out+="$q " |
|
| 63 |
done |
|
| 64 | ||
| 65 |
printf "%s" "$out" |
|
| 66 |
} |
|
| 67 | ||
| 68 |
resolve_ssh_config() {
|
|
| 69 |
local target=$1 |
|
| 70 |
local line |
|
| 71 | ||
| 72 |
target_user="" |
|
| 73 |
target_host="" |
|
| 74 |
target_port="" |
|
| 75 |
target_auth="" |
|
| 76 | ||
| 77 |
while IFS= read -r line; do |
|
| 78 |
case "$line" in |
|
| 79 |
user\ *) target_user=${line#user } ;;
|
|
| 80 |
hostname\ *) target_host=${line#hostname } ;;
|
|
| 81 |
port\ *) target_port=${line#port } ;;
|
|
| 82 |
setenv\ *NG_SSH_AUTH=password-interactive*) target_auth="password_interactive" ;; |
|
| 83 |
esac |
|
| 84 |
done < <("$real_ssh" ${ssh_config_args[@]+"${ssh_config_args[@]}"} -G "$target" 2>/dev/null)
|
|
| 85 | ||
| 86 |
[[ -n "$target_user" && -n "$target_host" && -n "$target_port" ]] |
|
| 87 |
} |
|
| 88 | ||
| 89 |
run_real_ssh() {
|
|
| 90 |
exec "$real_ssh" ${ssh_config_args[@]+"${ssh_config_args[@]}"} "$@"
|
|
| 91 |
} |
|
| 92 | ||
| 93 |
resolve_target_from_config() {
|
|
| 94 |
local target=$1 |
|
| 95 |
local default_user=${USER:-${LOGNAME:-}}
|
|
| 96 |
local user_override="" |
|
| 97 | ||
| 98 |
case "$target" in |
|
| 99 |
*@*) |
|
| 100 |
user_override=${target%@*}
|
|
| 101 |
target=${target#*@}
|
|
| 102 |
;; |
|
| 103 |
esac |
|
| 104 | ||
| 105 |
case "$target" in |
|
| 106 |
is-jumper|192.168.2.100) |
|
| 107 |
return 1 |
|
| 108 |
;; |
|
| 109 |
J1|J2|j1|j2) |
|
| 110 |
target_is_jump=1 |
|
| 111 |
;; |
|
| 112 |
esac |
|
| 113 | ||
| 114 |
resolve_ssh_config "$target" || return 1 |
|
| 115 | ||
| 116 |
if [[ -n "$user_override" ]]; then |
|
| 117 |
target_user=$user_override |
|
| 118 |
fi |
|
| 119 |
if [[ -n "$user_option" ]]; then |
|
| 120 |
target_user=$user_option |
|
| 121 |
fi |
|
| 122 |
if [[ -n "$port_option" ]]; then |
|
| 123 |
target_port=$port_option |
|
| 124 |
fi |
|
| 125 | ||
| 126 |
# Unconfigured host (ssh -G returns defaults): bypass unless a custom jump |
|
| 127 |
# was requested explicitly. |
|
| 128 |
if [[ "$target_is_jump" -eq 0 && "$target_host" == "$target" && "$target_port" == "22" && "$target_user" == "$default_user" ]]; then |
|
| 129 |
[[ $custom_jump_set -eq 0 ]] && return 1 |
|
| 130 |
host_configured=0 |
|
| 131 |
fi |
|
| 132 | ||
| 133 |
[[ -n "$target_user" && -n "$target_host" && -n "$target_port" ]] |
|
| 134 |
} |
|
| 135 | ||
| 136 |
resolve_jump() {
|
|
| 137 |
local saved_user saved_host saved_port saved_auth |
|
| 138 | ||
| 139 |
saved_user=$target_user |
|
| 140 |
saved_host=$target_host |
|
| 141 |
saved_port=$target_port |
|
| 142 |
saved_auth=$target_auth |
|
| 143 | ||
| 144 |
resolve_ssh_config "$jump_alias" || {
|
|
| 145 |
printf "ssh-wrapper: cannot resolve jump alias %s\n" "$jump_alias" >&2 |
|
| 146 |
exit 255 |
|
| 147 |
} |
|
| 148 | ||
| 149 |
jump_user=$target_user |
|
| 150 |
jump_host=$target_host |
|
| 151 |
jump_port=$target_port |
|
| 152 | ||
| 153 |
target_user=$saved_user |
|
| 154 |
target_host=$saved_host |
|
| 155 |
target_port=$saved_port |
|
| 156 |
target_auth=$saved_auth |
|
| 157 |
} |
|
| 158 | ||
| 159 |
# Pre-process: extract custom jump flags and strip them from args. |
|
| 160 |
filtered_args=() |
|
| 161 |
for arg in "$@"; do |
|
| 162 |
case "$arg" in |
|
| 163 |
-J1) jump_alias="j1"; custom_jump_set=1 ;; |
|
| 164 |
-J2) jump_alias="j2"; custom_jump_set=1 ;; |
|
| 165 |
-j1) jump_alias="j1"; custom_jump_set=1 ;; |
|
| 166 |
-j2) jump_alias="j2"; custom_jump_set=1 ;; |
|
| 167 |
*) filtered_args+=("$arg") ;;
|
|
| 168 |
esac |
|
| 169 |
done |
|
| 170 |
set -- "${filtered_args[@]+"${filtered_args[@]}"}"
|
|
| 171 | ||
| 172 |
if [[ -f "$ssh_config" ]] && ! has_explicit_config "$@"; then |
|
| 173 |
ssh_config_args=(-F "$ssh_config") |
|
| 174 |
fi |
|
| 175 | ||
| 176 |
skip_next=0 |
|
| 177 |
capture_user=0 |
|
| 178 |
capture_port=0 |
|
| 179 |
after_double_dash=0 |
|
| 180 | ||
| 181 |
for arg in "$@"; do |
|
| 182 |
if [[ $found_target -eq 1 ]]; then |
|
| 183 |
cmd_args+=("$arg")
|
|
| 184 |
continue |
|
| 185 |
fi |
|
| 186 | ||
| 187 |
if [[ $capture_user -eq 1 ]]; then |
|
| 188 |
user_option=$arg |
|
| 189 |
capture_user=0 |
|
| 190 |
continue |
|
| 191 |
fi |
|
| 192 | ||
| 193 |
if [[ $capture_port -eq 1 ]]; then |
|
| 194 |
port_option=$arg |
|
| 195 |
capture_port=0 |
|
| 196 |
continue |
|
| 197 |
fi |
|
| 198 | ||
| 199 |
if [[ $skip_next -eq 1 ]]; then |
|
| 200 |
skip_next=0 |
|
| 201 |
continue |
|
| 202 |
fi |
|
| 203 | ||
| 204 |
case "$arg" in |
|
| 205 |
-G|-Q|-V|-h|--help) |
|
| 206 |
run_real_ssh "$@" |
|
| 207 |
;; |
|
| 208 |
--) |
|
| 209 |
after_double_dash=1 |
|
| 210 |
continue |
|
| 211 |
;; |
|
| 212 |
esac |
|
| 213 | ||
| 214 |
if [[ $after_double_dash -eq 0 ]]; then |
|
| 215 |
case "$arg" in |
|
| 216 |
-s) |
|
| 217 |
want_subsystem=1 |
|
| 218 |
continue |
|
| 219 |
;; |
|
| 220 |
-l) |
|
| 221 |
capture_user=1 |
|
| 222 |
continue |
|
| 223 |
;; |
|
| 224 |
-l*) |
|
| 225 |
user_option=${arg#-l}
|
|
| 226 |
continue |
|
| 227 |
;; |
|
| 228 |
-p) |
|
| 229 |
capture_port=1 |
|
| 230 |
continue |
|
| 231 |
;; |
|
| 232 |
-p*) |
|
| 233 |
port_option=${arg#-p}
|
|
| 234 |
continue |
|
| 235 |
;; |
|
| 236 |
-b|-c|-D|-E|-e|-F|-I|-i|-J|-L|-m|-O|-o|-Q|-R|-S|-W|-w) |
|
| 237 |
skip_next=1 |
|
| 238 |
continue |
|
| 239 |
;; |
|
| 240 |
-b*|-c*|-D*|-E*|-e*|-F*|-I*|-i*|-J*|-L*|-m*|-O*|-o*|-Q*|-R*|-S*|-W*|-w*) |
|
| 241 |
continue |
|
| 242 |
;; |
|
| 243 |
-*) |
|
| 244 |
continue |
|
| 245 |
;; |
|
| 246 |
esac |
|
| 247 |
fi |
|
| 248 | ||
| 249 |
if ! resolve_target_from_config "$arg"; then |
|
| 250 |
run_real_ssh "$@" |
|
| 251 |
fi |
|
| 252 | ||
| 253 |
found_target=1 |
|
| 254 |
done |
|
| 255 | ||
| 256 |
if [[ $found_target -eq 0 ]]; then |
|
| 257 |
run_real_ssh "$@" |
|
| 258 |
fi |
|
| 259 | ||
| 260 |
tty_flag="-tt" |
|
| 261 |
if [[ ${#cmd_args[@]} -gt 0 || $want_subsystem -eq 1 ]]; then
|
|
| 262 |
tty_flag="-T" |
|
| 263 |
fi |
|
| 264 | ||
| 265 |
if [[ $target_is_jump -eq 1 ]]; then |
|
| 266 |
jump_cmd=(ssh "$tty_flag" -A -o BatchMode=yes -o ConnectTimeout=10 -o StrictHostKeyChecking=accept-new -p "$target_port" "$target_user@$target_host") |
|
| 267 |
jump_cmd+=(${cmd_args[@]+"${cmd_args[@]}"})
|
|
| 268 |
else |
|
| 269 |
resolve_jump |
|
| 270 | ||
| 271 |
final_cmd=(ssh "$tty_flag" -o ConnectTimeout=10 -o StrictHostKeyChecking=accept-new -o ProxyJump=none -o ProxyCommand=none) |
|
| 272 |
if [[ "$target_auth" == "password_interactive" ]]; then |
|
| 273 |
final_cmd+=( -o BatchMode=no -o PreferredAuthentications=keyboard-interactive,password -o PubkeyAuthentication=no ) |
|
| 274 |
elif [[ $host_configured -eq 1 ]]; then |
|
| 275 |
final_cmd+=( -o BatchMode=yes ) |
|
| 276 |
fi |
|
| 277 |
if [[ $want_subsystem -eq 1 ]]; then |
|
| 278 |
final_cmd+=( -s ) |
|
| 279 |
fi |
|
| 280 |
final_cmd+=( -p "$target_port" "$target_user@$target_host" ) |
|
| 281 |
final_cmd+=(${cmd_args[@]+"${cmd_args[@]}"})
|
|
| 282 | ||
| 283 |
jump_cmd=(ssh "$tty_flag" -A -o BatchMode=yes -o ConnectTimeout=10 -o StrictHostKeyChecking=accept-new -p "$jump_port" "$jump_user@$jump_host" "$(quote_cmd "${final_cmd[@]}")")
|
|
| 284 |
fi |
|
| 285 | ||
| 286 |
is_jumper_cmd="SSH_AUTH_SOCK=$remote_agent exec $(quote_cmd "${jump_cmd[@]}")"
|
|
| 287 | ||
| 288 |
exec "$real_ssh" ${ssh_config_args[@]+"${ssh_config_args[@]}"} "$tty_flag" -o BatchMode=yes -o ConnectTimeout=10 is-jumper "$is_jumper_cmd"
|