-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathbash_ssh
More file actions
760 lines (658 loc) · 31 KB
/
bash_ssh
File metadata and controls
760 lines (658 loc) · 31 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
#!/bin/bash
# ==============================================================================
# SSH MANAGEMENT
# ==============================================================================
# Comprehensive SSH management for bash-config including:
# - SSH config management (symlink to secrets)
# - SSH agent management (setup, cleanup, status)
# - Master connection management (multiplexing)
# - Daily work setup helper
# - Diamond SOCKS proxy support
#
# Quick Commands:
# ssh-setup - Full work day setup (config + agent + master connections)
# ssh-status - Show active master connections
# ssh-help - Detailed help for all SSH functions
#
# All functions are prefixed with bc_ for consistency.
# ==============================================================================
# ==============================================================================
# SSH CONFIG MANAGEMENT
# ==============================================================================
# Setup SSH config via symlink to secrets
bc_setup_ssh_config() {
local ssh_dir="$HOME/.ssh"
local config_file="$ssh_dir/config"
local secrets_ssh_config="$BASH_CONFIG_DIR/secrets/ssh_config"
local backup_suffix=".backup.$(date +%Y%m%d_%H%M%S)"
bc_log_debug "Setting up SSH config: $config_file -> $secrets_ssh_config"
# Ensure SSH directory exists
mkdir -p "$ssh_dir"
chmod 700 "$ssh_dir"
bc_log_debug "SSH directory created/verified: $ssh_dir"
# Check if secrets SSH config exists
if [[ ! -f "$secrets_ssh_config" ]]; then
bc_log_error "SSH config not found in secrets: $secrets_ssh_config"
return 1
fi
bc_log_debug "Secrets SSH config found: $secrets_ssh_config"
# Check if already correctly configured
if bc_check_ssh_config silent >/dev/null 2>&1; then
bc_log_info "SSH config already correctly configured"
return 0
fi
# Handle existing config file
if [[ -L "$config_file" ]]; then
local current_target=$(readlink "$config_file")
bc_log_info "Removing existing symlink (pointed to: $current_target)"
rm "$config_file"
elif [[ -f "$config_file" ]]; then
mv "$config_file" "${config_file}${backup_suffix}"
bc_log_info "Backed up existing SSH config to: ${config_file}${backup_suffix}"
fi
bc_log_debug "Handled existing config file"
# Create symlink and set permissions
ln -s "$secrets_ssh_config" "$config_file"
chmod 600 "$secrets_ssh_config"
bc_log_success "SSH config symlinked to secrets repository"
# Also set up SK SSH key handles
bc_setup_sk_ssh_handles
}
# Symlink YubiKey SK SSH key handles from secrets into ~/.ssh/
bc_setup_sk_ssh_handles() {
local ssh_dir="$HOME/.ssh"
local sk_dir="$BASH_CONFIG_DIR/secrets/sk_ssh_handles"
if [[ ! -d "$sk_dir" ]]; then
bc_log_debug "No SK SSH handles directory found: $sk_dir"
return 0
fi
mkdir -p "$ssh_dir"
chmod 700 "$ssh_dir"
local linked=0
local skipped=0
for handle in "$sk_dir"/id_ed25519_sk_*; do
[[ -f "$handle" ]] || continue
local name
name="$(basename "$handle")"
local target="$ssh_dir/$name"
if [[ -L "$target" ]] && [[ "$(readlink "$target")" == "$handle" ]]; then
bc_log_debug "SK handle already linked: $name"
((skipped++))
continue
fi
# Back up any existing non-symlink file
if [[ -f "$target" && ! -L "$target" ]]; then
mv "$target" "${target}.backup.$(date +%Y%m%d_%H%M%S)"
bc_log_info "Backed up existing $name"
fi
ln -sfn "$handle" "$target"
chmod 600 "$handle"
bc_log_debug "Linked SK handle: $name"
((linked++))
done
if [[ $linked -gt 0 ]]; then
bc_log_success "Linked $linked SK SSH key handle(s) into ~/.ssh/"
elif [[ $skipped -gt 0 ]]; then
bc_log_info "All $skipped SK SSH key handle(s) already linked"
else
bc_log_debug "No SK SSH key handles found to link"
fi
}
# SSH config validation helper
bc_check_ssh_config() {
local silent="${1:-false}"
local config_file="$HOME/.ssh/config"
local secrets_ssh_config="$BASH_CONFIG_DIR/secrets/ssh_config"
bc_log_debug "Checking SSH config: $config_file -> $secrets_ssh_config"
if [[ ! -f "$secrets_ssh_config" ]]; then
[[ "$silent" != "silent" ]] && bc_log_error "SSH config not found in secrets: $secrets_ssh_config"
return 1
fi
if [[ ! -L "$config_file" ]]; then
[[ "$silent" != "silent" ]] && bc_log_warn "SSH config is not a symlink to secrets"
bc_log_debug "Config file is not a symlink: $config_file"
return 1
fi
local target=$(readlink "$config_file")
if [[ "$target" != "$secrets_ssh_config" ]]; then
[[ "$silent" != "silent" ]] && bc_log_warn "SSH config symlink points to wrong location: $target"
bc_log_debug "Symlink target mismatch: expected $secrets_ssh_config, got $target"
return 1
fi
[[ "$silent" != "silent" ]] && bc_log_success "SSH config correctly symlinked to secrets"
bc_log_debug "SSH config validation passed"
return 0
}
# Fix SSH directory and file permissions to meet OpenSSH's strict requirements
bc_fix_ssh_permissions() {
local ssh_dir="$HOME/.ssh"
chmod 700 "$ssh_dir" # Only owner can read/write/enter
chmod 600 "$ssh_dir"/config 2>/dev/null # Only owner can read/write SSH config
chmod 600 "$ssh_dir"/id_* 2>/dev/null # Private keys: owner read/write only
chmod 644 "$ssh_dir"/id_*.pub 2>/dev/null # Public keys: owner read/write, others read
chmod 600 "$ssh_dir"/authorized_keys 2>/dev/null # Authorised keys: owner read/write only
chmod 600 "$ssh_dir"/known_hosts 2>/dev/null # Known hosts: owner read/write only
# Also fix permissions on the secrets config (symlink target)
if [[ -n "$BASH_CONFIG_DIR" && -f "$BASH_CONFIG_DIR/secrets/ssh_config" ]]; then
chmod 600 "$BASH_CONFIG_DIR/secrets/ssh_config" # Symlink target must also be 600
fi
bc_log_success "SSH permissions fixed"
}
# ==============================================================================
# MASTER CONNECTION MANAGEMENT
# ==============================================================================
# Simple master connection management
bc_ssh_master_start() {
local host="$1"
if [[ -z "$host" ]]; then
bc_log_error "Usage: bc_ssh_master_start <hostname>"
return 1
fi
bc_log_debug "Attempting to start master connection to: $host"
bc_log_info "Starting master connection to $host..."
if ssh -MNf "$host"; then
bc_log_success "Master connection to $host started"
bc_log_debug "Master connection process backgrounded successfully"
else
bc_log_error "Failed to start master connection to $host"
bc_log_debug "SSH master connection command failed with exit code: $?"
return 1
fi
}
bc_ssh_master_status() {
bc_log_debug "Checking for active SSH master connections in ~/.ssh/cm-*"
bc_log_info "Active SSH master connections:"
if ls ~/.ssh/cm-* 2>/dev/null | head -1 >/dev/null; then
bc_log_debug "Found master connection control files"
ls -la ~/.ssh/cm-* 2>/dev/null
else
bc_log_info "No active master connections found"
bc_log_debug "No control files matching ~/.ssh/cm-* pattern"
fi
}
# ==============================================================================
# DAILY WORK SETUP
# ==============================================================================
# Daily work setup - main function for setting up SSH for work
bc_ssh_workday_setup() {
local start_proxy="${1:-false}"
local proxy_port="${2:-8080}"
bc_log_info "🚀 Setting up SSH for work day..."
echo
# 1. Setup SSH agent (includes automatic cleanup)
bc_log_info "🔐 Setting up SSH agent..."
bc_setup_ssh_agent
echo
# 2. Validate/setup SSH config
bc_log_info "📋 Checking SSH configuration..."
if ! bc_check_ssh_config silent; then
bc_log_info "Setting up SSH config..."
bc_setup_ssh_config || return 1
else
bc_log_success "SSH config is ready"
fi
echo
# 3. Ensure SK SSH key handles are symlinked
bc_log_info "🔑 Checking YubiKey SK key handles..."
bc_setup_sk_ssh_handles
echo
# 4. Start master connection to bastion
bc_log_info "🏢 Starting bastion master connection..."
bc_ssh_master_start bastion
echo
# 5. Show status
bc_log_info "📊 Connection status:"
bc_ssh_master_status
echo
bc_log_success "🎉 Work day SSH setup complete!"
bc_log_info "💡 All SSH connections will now use the shared master connection"
# 6. Optionally start Diamond proxy
if [[ "$start_proxy" == "true" || "$start_proxy" == "proxy" ]]; then
echo
bc_log_info "🌐 Starting Diamond SOCKS proxy..."
diamond-proxy "$proxy_port"
fi
}
# ==============================================================================
# SSH AGENT MANAGEMENT
# ==============================================================================
# Check if a specific SSH key is loaded in the agent
bc_is_ssh_key_loaded() {
local key_file="$1"
if [[ -z "$key_file" || ! -f "$key_file" ]]; then
bc_log_debug "Invalid key file provided: $key_file"
return 1
fi
bc_log_debug "Checking if SSH key is loaded: $key_file"
local key_fingerprint
key_fingerprint=$(ssh-keygen -lf "$key_file" 2>/dev/null | awk '{print $2}')
bc_log_debug "Key fingerprint: $key_fingerprint"
if [[ -n "$key_fingerprint" ]] && ssh-add -l 2>/dev/null | grep -q "$key_fingerprint"; then
bc_log_debug "Key is loaded in agent"
return 0
else
bc_log_debug "Key is not loaded in agent"
return 1
fi
}
# Start a new SSH agent and save info to file
bc_start_ssh_agent() {
local agent_file="$1"
bc_log_debug "Starting new SSH agent, will save info to: $agent_file"
eval "$(ssh-agent -s)" >&2
bc_log_debug "SSH agent started with PID: $SSH_AGENT_PID, Socket: $SSH_AUTH_SOCK"
echo "SSH_AUTH_SOCK=$SSH_AUTH_SOCK; export SSH_AUTH_SOCK;" > "$agent_file"
echo "SSH_AGENT_PID=$SSH_AGENT_PID; export SSH_AGENT_PID;" >> "$agent_file"
bc_log_debug "Agent info saved to file: $agent_file"
bc_log_debug "File contents: $(cat "$agent_file")"
}
# Unified SSH agent cleanup function
# Usage: bc_cleanup_ssh_agents [agent_file] [mode] [interactive]
# Modes: "orphaned" (default), "current", "stored", "all"
# Interactive: "true" (default for non-orphaned), "false" (silent), "confirm" (ask first)
bc_cleanup_ssh_agents() {
local agent_file="${1:-$HOME/.ssh-agent-info}"
local mode="${2:-orphaned}"
local interactive="${3:-auto}"
# Set interactive mode based on cleanup type
if [[ "$interactive" == "auto" ]]; then
case "$mode" in
"orphaned") interactive="false" ;; # Silent for orphaned cleanup
"all") interactive="confirm" ;; # Always confirm for all
*) interactive="true" ;; # Default logging for others
esac
fi
# Get all running ssh-agent processes for this user
local current_agents
mapfile -t current_agents < <(pgrep -u "$USER" ssh-agent 2>/dev/null)
bc_log_debug "Found ${#current_agents[@]} ssh-agent processes for user $USER"
if [[ ${#current_agents[@]} -eq 0 ]]; then
[[ "$interactive" != "false" ]] && bc_log_info "No ssh-agent processes found"
return 0
fi
bc_log_debug "Agent PIDs: ${current_agents[*]}"
# Get agent info
local stored_pid=""
if [[ -f "$agent_file" ]]; then
stored_pid=$(grep "SSH_AGENT_PID=" "$agent_file" 2>/dev/null | cut -d'=' -f2 | cut -d';' -f1 | tr -d ' ')
bc_log_debug "Stored agent PID from file: $stored_pid"
fi
local current_pid="${SSH_AGENT_PID:-}"
bc_log_debug "Current session agent PID: $current_pid"
# Confirmation for destructive operations
if [[ "$interactive" == "confirm" ]]; then
local msg=""
case "$mode" in
"all") msg="This will kill ALL ssh-agent processes for user $USER" ;;
"current") msg="This will kill the current session's SSH agent" ;;
"stored") msg="This will kill the stored SSH agent" ;;
esac
if [[ -n "$msg" ]]; then
bc_log_warn "$msg"
read -p "Are you sure? (y/N): " -n 1 -r
echo
if [[ ! $REPLY =~ ^[Yy]$ ]]; then
bc_log_info "Cleanup cancelled"
bc_log_debug "User cancelled cleanup operation"
return 0
fi
bc_log_debug "User confirmed cleanup operation"
fi
fi
[[ "$interactive" == "true" ]] && bc_log_info "Cleaning up SSH agents (mode: $mode)"
bc_log_debug "Starting cleanup with mode: $mode, interactive: $interactive"
local killed_count=0
for pid in "${current_agents[@]}"; do
local should_kill=false
local reason=""
case "$mode" in
"all")
should_kill=true
reason="force cleanup"
;;
"current")
if [[ -n "$current_pid" && "$pid" == "$current_pid" ]]; then
should_kill=true
reason="current session agent"
fi
;;
"stored")
if [[ -n "$stored_pid" && "$pid" == "$stored_pid" ]]; then
should_kill=true
reason="stored agent"
fi
;;
"orphaned")
# Default orphaned cleanup logic
if [[ -n "$stored_pid" && "$pid" == "$stored_pid" ]]; then
# Test if stored agent is actually working
bc_log_debug "Testing stored agent connectivity (PID: $pid)"
# First check if process is even running
if ! kill -0 "$stored_pid" 2>/dev/null; then
should_kill=true
reason="stored agent process is dead"
bc_log_debug "Stored agent process is not running, marking for cleanup"
else
# Process is running, test if socket is accessible
local stored_sock
stored_sock=$(grep "SSH_AUTH_SOCK=" "$agent_file" 2>/dev/null | cut -d'=' -f2 | cut -d';' -f1)
if [[ -S "$stored_sock" ]]; then
# Test connectivity - exit codes 0 and 1 are both acceptable
local ssh_add_exit_code
SSH_AUTH_SOCK="$stored_sock" SSH_AGENT_PID="$stored_pid" timeout 2 ssh-add -l >/dev/null 2>&1
ssh_add_exit_code=$?
if [[ $ssh_add_exit_code -eq 0 || $ssh_add_exit_code -eq 1 ]]; then
bc_log_debug "Stored agent is working (exit code: $ssh_add_exit_code), keeping it"
continue # Agent is working, keep it
else
should_kill=true
reason="stored agent not responding (exit code: $ssh_add_exit_code)"
bc_log_debug "Stored agent not responding, marking for cleanup"
fi
else
should_kill=true
reason="stored agent socket file missing"
bc_log_debug "Stored agent socket file does not exist, marking for cleanup"
fi
fi
elif [[ -n "$current_pid" && "$pid" == "$current_pid" ]]; then
bc_log_debug "Skipping current session agent (PID: $pid)"
continue # Don't kill current session agent
else
should_kill=true
reason="orphaned agent"
bc_log_debug "Found orphaned agent (PID: $pid)"
fi
;;
esac
bc_log_debug "Processing PID $pid: should_kill=$should_kill, reason='$reason'"
if [[ "$should_kill" == "true" ]]; then
if kill "$pid" 2>/dev/null; then
if [[ "$interactive" == "false" ]]; then
bc_log_debug "Cleaned up $reason (PID: $pid)"
else
bc_log_debug "Killed $reason (PID: $pid)"
fi
((killed_count++))
fi
fi
done
# Clean up agent info file if appropriate
if [[ -f "$agent_file" && -n "$stored_pid" ]]; then
local should_remove_file=false
case "$mode" in
"all"|"stored")
should_remove_file=true
;;
"orphaned"|"current")
# Remove file if we killed the stored agent or current agent
if [[ ($killed_count -gt 0) && ((-n "$stored_pid" && "$mode" == "orphaned") || (-n "$current_pid" && "$mode" == "current")) ]]; then
should_remove_file=true
fi
;;
esac
if [[ "$should_remove_file" == "true" ]]; then
rm "$agent_file"
bc_log_debug "Removed agent info file"
fi
fi
# Report results
bc_log_debug "Cleanup completed: killed $killed_count agent(s)"
if [[ $killed_count -gt 0 ]]; then
if [[ "$interactive" == "false" ]]; then
bc_log_debug "Auto-cleaned up $killed_count ssh-agent process(es)"
else
bc_log_success "Cleaned up $killed_count ssh-agent process(es)"
fi
else
if [[ "$interactive" == "true" ]]; then
bc_log_warn "No ssh-agent processes were cleaned up"
if [[ ${#current_agents[@]} -gt 0 && "$mode" != "all" ]]; then
bc_log_info "Use 'bc_cleanup_ssh_agents \"$agent_file\" all' to kill all agents"
fi
fi
fi
}
# Setup empty SSH agent for connection multiplexing
bc_setup_ssh_agent() {
local agent_file="${1:-$HOME/.ssh-agent-info}"
bc_log_debug "Setting up SSH agent for connection multiplexing"
# Try to find and load existing agent info first (before cleanup)
if [[ -f "$agent_file" ]]; then
bc_log_debug "Found existing agent info file, attempting to reuse"
# Extract stored agent info
local stored_sock stored_pid
stored_sock=$(grep "SSH_AUTH_SOCK=" "$agent_file" 2>/dev/null | cut -d'=' -f2 | cut -d';' -f1)
stored_pid=$(grep "SSH_AGENT_PID=" "$agent_file" 2>/dev/null | cut -d'=' -f2 | cut -d';' -f1 | tr -d ' ')
bc_log_debug "Stored agent info - PID: $stored_pid, Socket: $stored_sock"
# Test if stored agent is accessible before loading it
if [[ -n "$stored_pid" && -n "$stored_sock" ]] && kill -0 "$stored_pid" 2>/dev/null; then
bc_log_debug "Stored agent process is running, testing socket connectivity"
if [[ -S "$stored_sock" ]]; then
# Test agent connectivity - exit codes: 0=has keys, 1=no keys (both are good), 2=can't connect (bad)
local ssh_add_exit_code
SSH_AUTH_SOCK="$stored_sock" SSH_AGENT_PID="$stored_pid" timeout 2 ssh-add -l >/dev/null 2>&1
ssh_add_exit_code=$?
if [[ $ssh_add_exit_code -eq 0 || $ssh_add_exit_code -eq 1 ]]; then
# Agent is working (either has keys or no keys - both are fine)
bc_log_debug "Stored agent is working (exit code: $ssh_add_exit_code), loading into current session"
export SSH_AUTH_SOCK="$stored_sock"
export SSH_AGENT_PID="$stored_pid"
bc_log_debug "Loaded agent environment - PID: $SSH_AGENT_PID, Socket: $SSH_AUTH_SOCK"
# Now run cleanup to remove any OTHER orphaned agents (but not our working one)
bc_cleanup_ssh_agents "$agent_file" "orphaned" "false"
return 0
else
# Agent is not responding (exit code 2 or timeout)
bc_log_debug "Stored agent not responding (exit code: $ssh_add_exit_code), removing stale agent info file"
rm "$agent_file" 2>/dev/null
bc_start_ssh_agent "$agent_file"
fi
else
bc_log_debug "Stored agent socket file does not exist: $stored_sock"
bc_log_debug "Removing stale agent info file and starting new agent"
rm "$agent_file" 2>/dev/null
bc_start_ssh_agent "$agent_file"
fi
else
bc_log_debug "Stored agent process not running or invalid info - PID exists: $(kill -0 "$stored_pid" 2>/dev/null && echo "yes" || echo "no")"
bc_log_debug "Removing stale agent info file and starting new agent"
rm "$agent_file" 2>/dev/null
bc_start_ssh_agent "$agent_file"
fi
else
# No agent file exists, clean up any orphaned agents and start fresh
bc_cleanup_ssh_agents "$agent_file" "orphaned" "false"
bc_log_debug "No agent info file found, starting new agent"
bc_start_ssh_agent "$agent_file"
fi
# Set up cleanup on exit for interactive shells
if [[ $- == *i* ]]; then
bc_log_debug "Setting up agent cleanup trap for interactive shell"
bc_cleanup_ssh_agent_on_exit() {
if [[ -n "${SSH_AGENT_PID:-}" ]]; then
bc_log_debug "Cleaning up SSH agent on exit (PID: $SSH_AGENT_PID)"
kill "$SSH_AGENT_PID" 2>/dev/null || true
fi
}
trap bc_cleanup_ssh_agent_on_exit EXIT
else
bc_log_debug "Non-interactive shell, skipping cleanup trap"
fi
}
# ==============================================================================
# CONVENIENCE WRAPPERS AND ALIASES
# ==============================================================================
# Convenience wrapper functions
bc_cleanup_current_ssh_agent() {
local agent_file="${1:-$HOME/.ssh-agent-info}"
bc_log_debug "Cleanup current agent wrapper called with agent_file: $agent_file"
bc_cleanup_ssh_agents "$agent_file" "current" "true"
}
bc_cleanup_all_ssh_agents() {
local agent_file="${1:-$HOME/.ssh-agent-info}"
bc_log_debug "Cleanup all agents wrapper called with agent_file: $agent_file"
bc_cleanup_ssh_agents "$agent_file" "all" "confirm"
}
# List all SSH agents and their status
bc_list_ssh_agents() {
local agent_file="${1:-$HOME/.ssh-agent-info}"
local current_agents
bc_log_info "SSH Agent Status"
# Get all running ssh-agent processes for this user
mapfile -t current_agents < <(pgrep -u "$USER" ssh-agent 2>/dev/null)
if [[ ${#current_agents[@]} -eq 0 ]]; then
echo -e " ${BC_COLOR_YELLOW}No ssh-agent processes found${BC_COLOR_RESET}"
return 0
fi
# Get the PID from our agent file if it exists
local stored_pid=""
if [[ -f "$agent_file" ]]; then
stored_pid=$(grep "SSH_AGENT_PID=" "$agent_file" 2>/dev/null | cut -d'=' -f2 | cut -d';' -f1 | tr -d ' ')
fi
# Get current agent PID from environment
local current_pid="${SSH_AGENT_PID:-}"
echo -e " ${BC_COLOR_BLUE}Found ${#current_agents[@]} ssh-agent process(es):${BC_COLOR_RESET}"
for pid in "${current_agents[@]}"; do
local status_info=""
local color="$BC_COLOR_GRAY"
if [[ -n "$stored_pid" && "$pid" == "$stored_pid" ]]; then
status_info=" (stored)"
color="$BC_COLOR_GREEN"
fi
if [[ -n "$current_pid" && "$pid" == "$current_pid" ]]; then
status_info=" (current session)"
color="$BC_COLOR_GREEN"
fi
if [[ -z "$status_info" ]]; then
status_info=" (orphaned)"
color="$BC_COLOR_YELLOW"
fi
echo -e " ${color}PID $pid${status_info}${BC_COLOR_RESET}"
done
# Show current session info
echo -e " ${BC_COLOR_BLUE}Current session:${BC_COLOR_RESET}"
if [[ -n "$SSH_AUTH_SOCK" ]]; then
echo -e " ${BC_COLOR_GREEN}SSH_AUTH_SOCK: $SSH_AUTH_SOCK${BC_COLOR_RESET}"
if [[ -n "$SSH_AGENT_PID" ]]; then
echo -e " ${BC_COLOR_GREEN}SSH_AGENT_PID: $SSH_AGENT_PID${BC_COLOR_RESET}"
else
echo -e " ${BC_COLOR_YELLOW}SSH_AGENT_PID: not set${BC_COLOR_RESET}"
fi
# Try to list loaded keys
if ssh-add -l >/dev/null 2>&1; then
local key_count=$(ssh-add -l 2>/dev/null | wc -l)
echo -e " ${BC_COLOR_GREEN}Status: Active with $key_count key(s) loaded${BC_COLOR_RESET}"
else
echo -e " ${BC_COLOR_YELLOW}Status: Socket exists but no keys loaded${BC_COLOR_RESET}"
fi
else
echo -e " ${BC_COLOR_RED}No SSH agent configured for this session${BC_COLOR_RESET}"
fi
# Show stored agent info
if [[ -f "$agent_file" ]]; then
echo -e " ${BC_COLOR_BLUE}Stored agent info (${agent_file}):${BC_COLOR_RESET}"
if [[ -n "$stored_pid" ]]; then
if kill -0 "$stored_pid" 2>/dev/null; then
echo -e " ${BC_COLOR_GREEN}PID $stored_pid (running)${BC_COLOR_RESET}"
else
echo -e " ${BC_COLOR_RED}PID $stored_pid (dead)${BC_COLOR_RESET}"
fi
else
echo -e " ${BC_COLOR_RED}Invalid agent file format${BC_COLOR_RESET}"
fi
else
echo -e " ${BC_COLOR_YELLOW}No stored agent info file${BC_COLOR_RESET}"
fi
}
# ==============================================================================
# HELP SYSTEM
# ==============================================================================
# SSH agent management help
bc_ssh_help() {
echo -e "${BC_COLOR_CYAN}🔑 SSH Management Help:${BC_COLOR_RESET}"
echo
echo -e "${BC_COLOR_BLUE}Connection Management:${BC_COLOR_RESET}"
echo -e " ${BC_COLOR_GREEN}bc_ssh_workday_setup${BC_COLOR_RESET}"
echo -e " Complete work day setup (config + master connections)"
echo -e " ${BC_COLOR_GREEN}bc_setup_ssh_config${BC_COLOR_RESET}"
echo -e " Set up SSH config symlink to secrets (also links SK handles)"
echo -e " ${BC_COLOR_GREEN}bc_setup_sk_ssh_handles${BC_COLOR_RESET}"
echo -e " Symlink YubiKey SK key handles from secrets into ~/.ssh/"
echo -e " ${BC_COLOR_GREEN}bc_check_ssh_config [silent]${BC_COLOR_RESET}"
echo -e " Validate SSH config setup"
echo -e " ${BC_COLOR_GREEN}bc_fix_ssh_permissions${BC_COLOR_RESET}"
echo -e " Fix ~/.ssh directory and file permissions to meet OpenSSH requirements"
echo -e " ${BC_COLOR_GREEN}bc_ssh_master_start <host>${BC_COLOR_RESET}"
echo -e " Start master connection to host"
echo -e " ${BC_COLOR_GREEN}bc_ssh_master_status${BC_COLOR_RESET}"
echo -e " Show active master connections"
echo
echo -e "${BC_COLOR_BLUE}Agent Management:${BC_COLOR_RESET}"
echo -e " ${BC_COLOR_GREEN}bc_setup_ssh_agent [agent_file]${BC_COLOR_RESET}"
echo -e " Set up an empty SSH agent for connection multiplexing (auto-cleans orphaned agents)"
echo -e " ${BC_COLOR_GREEN}bc_cleanup_ssh_agents [agent_file] [mode] [interactive]${BC_COLOR_RESET}"
echo -e " Clean up SSH agents. Modes: orphaned (default), current, stored, all"
echo -e " ${BC_COLOR_GREEN}bc_list_ssh_agents [agent_file]${BC_COLOR_RESET}"
echo -e " Show status of all SSH agents and current session info"
echo -e " ${BC_COLOR_GREEN}bc_is_ssh_key_loaded <key_file>${BC_COLOR_RESET}"
echo -e " Check if a specific SSH key is loaded in the current agent"
echo -e " ${BC_COLOR_GREEN}bc_cleanup_current_ssh_agent [agent_file]${BC_COLOR_RESET}"
echo -e " Shortcut: clean up only the current session's SSH agent"
echo -e " ${BC_COLOR_GREEN}bc_cleanup_all_ssh_agents [agent_file]${BC_COLOR_RESET}"
echo -e " Shortcut: force cleanup all SSH agents with confirmation prompt"
echo
echo -e "${BC_COLOR_BLUE}Quick Aliases:${BC_COLOR_RESET}"
echo -e " ${BC_COLOR_GREEN}ssh-setup${BC_COLOR_RESET} → bc_ssh_workday_setup"
echo -e " ${BC_COLOR_GREEN}ssh-fix-perms${BC_COLOR_RESET} → bc_fix_ssh_permissions"
echo -e " ${BC_COLOR_GREEN}ssh-status${BC_COLOR_RESET} → bc_ssh_master_status"
echo -e " ${BC_COLOR_GREEN}ssh-master${BC_COLOR_RESET} → bc_ssh_master_start"
echo -e " ${BC_COLOR_GREEN}ssh-help${BC_COLOR_RESET} → bc_ssh_help"
echo
echo -e "${BC_COLOR_BLUE}Examples:${BC_COLOR_RESET}"
echo -e " ${BC_COLOR_GRAY}# Full work day setup${BC_COLOR_RESET}"
echo -e " ssh-setup"
echo -e " ${BC_COLOR_GRAY}# Check agent status${BC_COLOR_RESET}"
echo -e " bc_list_ssh_agents"
echo -e " ${BC_COLOR_GRAY}# Clean up specific agent types${BC_COLOR_RESET}"
echo -e " bc_cleanup_ssh_agents ~/.ssh-agent-info orphaned"
echo -e " bc_cleanup_ssh_agents ~/.ssh-agent-info current"
echo -e " bc_cleanup_ssh_agents ~/.ssh-agent-info all confirm"
echo -e " ${BC_COLOR_GRAY}# Load a key and check if it's loaded${BC_COLOR_RESET}"
echo -e " ssh-add ~/.ssh/id_rsa"
echo -e " bc_is_ssh_key_loaded ~/.ssh/id_rsa"
echo
echo -e "${BC_COLOR_BLUE}Notes:${BC_COLOR_RESET}"
echo -e " • The agent is set up for connection multiplexing, not key storage"
echo -e " • SSH keys are managed via symlinked portable SSH config"
echo -e " • Agent info is stored persistently across sessions"
echo -e " • Cleanup functions prioritize safety over convenience"
}
# ==============================================================================
# DIAMOND-SPECIFIC PROXY FUNCTIONS
# ==============================================================================
# Diamond SOCKS proxy functions (for accessing Diamond intranet from local machine)
# Start Diamond SOCKS proxy for web browsing
diamond-proxy() {
local port="${1:-8080}"
bc_log_info "Starting SOCKS proxy on port $port..."
echo "Configure FoxyProxy: SOCKS5 127.0.0.1:$port"
echo "For Diamond intranet access, use patterns: *.diamond.ac.uk, *.diamond"
echo "Press Ctrl+C to stop the proxy"
ssh -D "$port" -C -N diamond-proxy
}
# List all active Diamond SSH connections
diamond-proxy-list() {
bc_log_info "Active Diamond SSH connections:"
pgrep -af "ssh.*diamond" 2>/dev/null || echo "No active Diamond SSH connections found"
}
# ==============================================================================
# QUICK ALIASES
# ==============================================================================
# Simple aliases for common operations
alias ssh-setup='bc_ssh_workday_setup proxy'
alias ssh-setup-only='bc_ssh_workday_setup false'
alias ssh-fix-perms='bc_fix_ssh_permissions'
alias ssh-status='bc_ssh_master_status'
alias ssh-master='bc_ssh_master_start'
alias ssh-help='bc_ssh_help'