Newer Older
303 lines | 7.922kb
Bogdan Timofte authored 2 weeks ago
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=""
Bogdan Timofte authored 2 weeks ago
97
    local target_route=""
Bogdan Timofte authored 2 weeks ago
98

            
99
    case "$target" in
100
        *@*)
101
            user_override=${target%@*}
102
            target=${target#*@}
103
            ;;
104
    esac
105

            
106
    case "$target" in
107
        is-jumper|192.168.2.100)
108
            return 1
109
            ;;
110
        J1|J2|j1|j2)
111
            target_is_jump=1
112
            ;;
113
    esac
114

            
115
    resolve_ssh_config "$target" || return 1
116

            
Bogdan Timofte authored 2 weeks ago
117
    # Check for SSH_ROUTE in config
118
    while IFS= read -r line; do
119
        case "$line" in
120
            setenv\ SSH_ROUTE=*)
121
                target_route=${line#setenv SSH_ROUTE=}
122
                ;;
123
        esac
124
    done < <("$real_ssh" ${ssh_config_args[@]+"${ssh_config_args[@]}"} -G "$target" 2>/dev/null)
125

            
126
    # If route is "local", no jump needed
127
    if [[ "$target_route" == "local" ]]; then
128
        return 1
129
    fi
130

            
Bogdan Timofte authored 2 weeks ago
131
    if [[ -n "$user_override" ]]; then
132
        target_user=$user_override
133
    fi
134
    if [[ -n "$user_option" ]]; then
135
        target_user=$user_option
136
    fi
137
    if [[ -n "$port_option" ]]; then
138
        target_port=$port_option
139
    fi
140

            
141
    # Unconfigured host (ssh -G returns defaults): bypass unless a custom jump
142
    # was requested explicitly.
143
    if [[ "$target_is_jump" -eq 0 && "$target_host" == "$target" && "$target_port" == "22" && "$target_user" == "$default_user" ]]; then
144
        [[ $custom_jump_set -eq 0 ]] && return 1
145
        host_configured=0
146
    fi
147

            
148
    [[ -n "$target_user" && -n "$target_host" && -n "$target_port" ]]
149
}
150

            
151
resolve_jump() {
152
    local saved_user saved_host saved_port saved_auth
153

            
154
    saved_user=$target_user
155
    saved_host=$target_host
156
    saved_port=$target_port
157
    saved_auth=$target_auth
158

            
159
    resolve_ssh_config "$jump_alias" || {
160
        printf "ssh-wrapper: cannot resolve jump alias %s\n" "$jump_alias" >&2
161
        exit 255
162
    }
163

            
164
    jump_user=$target_user
165
    jump_host=$target_host
166
    jump_port=$target_port
167

            
168
    target_user=$saved_user
169
    target_host=$saved_host
170
    target_port=$saved_port
171
    target_auth=$saved_auth
172
}
173

            
174
# Pre-process: extract custom jump flags and strip them from args.
175
filtered_args=()
176
for arg in "$@"; do
177
    case "$arg" in
178
        -J1) jump_alias="j1"; custom_jump_set=1 ;;
179
        -J2) jump_alias="j2"; custom_jump_set=1 ;;
180
        -j1) jump_alias="j1"; custom_jump_set=1 ;;
181
        -j2) jump_alias="j2"; custom_jump_set=1 ;;
182
        *) filtered_args+=("$arg") ;;
183
    esac
184
done
185
set -- "${filtered_args[@]+"${filtered_args[@]}"}"
186

            
187
if [[ -f "$ssh_config" ]] && ! has_explicit_config "$@"; then
188
    ssh_config_args=(-F "$ssh_config")
189
fi
190

            
191
skip_next=0
192
capture_user=0
193
capture_port=0
194
after_double_dash=0
195

            
196
for arg in "$@"; do
197
    if [[ $found_target -eq 1 ]]; then
198
        cmd_args+=("$arg")
199
        continue
200
    fi
201

            
202
    if [[ $capture_user -eq 1 ]]; then
203
        user_option=$arg
204
        capture_user=0
205
        continue
206
    fi
207

            
208
    if [[ $capture_port -eq 1 ]]; then
209
        port_option=$arg
210
        capture_port=0
211
        continue
212
    fi
213

            
214
    if [[ $skip_next -eq 1 ]]; then
215
        skip_next=0
216
        continue
217
    fi
218

            
219
    case "$arg" in
220
        -G|-Q|-V|-h|--help)
221
            run_real_ssh "$@"
222
            ;;
223
        --)
224
            after_double_dash=1
225
            continue
226
            ;;
227
    esac
228

            
229
    if [[ $after_double_dash -eq 0 ]]; then
230
        case "$arg" in
231
            -s)
232
                want_subsystem=1
233
                continue
234
                ;;
235
            -l)
236
                capture_user=1
237
                continue
238
                ;;
239
            -l*)
240
                user_option=${arg#-l}
241
                continue
242
                ;;
243
            -p)
244
                capture_port=1
245
                continue
246
                ;;
247
            -p*)
248
                port_option=${arg#-p}
249
                continue
250
                ;;
251
            -b|-c|-D|-E|-e|-F|-I|-i|-J|-L|-m|-O|-o|-Q|-R|-S|-W|-w)
252
                skip_next=1
253
                continue
254
                ;;
255
            -b*|-c*|-D*|-E*|-e*|-F*|-I*|-i*|-J*|-L*|-m*|-O*|-o*|-Q*|-R*|-S*|-W*|-w*)
256
                continue
257
                ;;
258
            -*)
259
                continue
260
                ;;
261
        esac
262
    fi
263

            
264
    if ! resolve_target_from_config "$arg"; then
265
        run_real_ssh "$@"
266
    fi
267

            
268
    found_target=1
269
done
270

            
271
if [[ $found_target -eq 0 ]]; then
272
    run_real_ssh "$@"
273
fi
274

            
275
tty_flag="-tt"
276
if [[ ${#cmd_args[@]} -gt 0 || $want_subsystem -eq 1 ]]; then
277
    tty_flag="-T"
278
fi
279

            
280
if [[ $target_is_jump -eq 1 ]]; then
281
    jump_cmd=(ssh "$tty_flag" -A -o BatchMode=yes -o ConnectTimeout=10 -o StrictHostKeyChecking=accept-new -p "$target_port" "$target_user@$target_host")
282
    jump_cmd+=(${cmd_args[@]+"${cmd_args[@]}"})
283
else
284
    resolve_jump
285

            
286
    final_cmd=(ssh "$tty_flag" -o ConnectTimeout=10 -o StrictHostKeyChecking=accept-new -o ProxyJump=none -o ProxyCommand=none)
287
    if [[ "$target_auth" == "password_interactive" ]]; then
288
        final_cmd+=( -o BatchMode=no -o PreferredAuthentications=keyboard-interactive,password -o PubkeyAuthentication=no )
289
    elif [[ $host_configured -eq 1 ]]; then
290
        final_cmd+=( -o BatchMode=yes )
291
    fi
292
    if [[ $want_subsystem -eq 1 ]]; then
293
        final_cmd+=( -s )
294
    fi
295
    final_cmd+=( -p "$target_port" "$target_user@$target_host" )
296
    final_cmd+=(${cmd_args[@]+"${cmd_args[@]}"})
297

            
298
    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[@]}")")
299
fi
300

            
301
is_jumper_cmd="SSH_AUTH_SOCK=$remote_agent exec $(quote_cmd "${jump_cmd[@]}")"
302

            
303
exec "$real_ssh" ${ssh_config_args[@]+"${ssh_config_args[@]}"} "$tty_flag" -o BatchMode=yes -o ConnectTimeout=10 is-jumper "$is_jumper_cmd"