Skip to content

Commit 77a2768

Browse files
committed
fix(harden): replace gum prompts with plain bash prompts
gum was silently auto-skipping all confirmations due to TTY being consumed. Replaced all gum_confirm/gum_choose/gum_input calls with prompt_yes_no and read, removed ensure_gum_for_ui dependency.
1 parent afdb849 commit 77a2768

3 files changed

Lines changed: 91 additions & 156 deletions

File tree

CHANGELOG.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,11 @@ All notable changes to ShipNode will be documented in this file.
55
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
66
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
77

8+
## [1.4.13] - 2026-05-11
9+
10+
### Fixed
11+
- **harden**: Replaced all `gum` prompts with plain `read`/`prompt_yes_no` — gum was silently auto-skipping all confirmations due to TTY consumption
12+
813
## [1.4.12] - 2026-05-11
914

1015
### Fixed

lib/commands/harden.sh

Lines changed: 85 additions & 155 deletions
Original file line numberDiff line numberDiff line change
@@ -233,7 +233,6 @@ ENDSSH
233233
# Main harden command
234234
cmd_harden() {
235235
load_config
236-
ensure_gum_for_ui
237236

238237
info "ShipNode Security Hardening"
239238
info "This wizard will guide you through basic server hardening."
@@ -249,7 +248,8 @@ cmd_harden() {
249248
echo ""
250249

251250
# Check SSH service status
252-
local ssh_status=$(check_ssh_service)
251+
local ssh_status
252+
ssh_status=$(check_ssh_service)
253253
if [[ "$ssh_status" == *"NOT_FOUND"* ]]; then
254254
warn "SSH service not detected on remote server"
255255
elif [[ "$ssh_status" == *"INACTIVE"* ]]; then
@@ -259,275 +259,205 @@ cmd_harden() {
259259

260260
local changes_made=0
261261
local backup_path=""
262+
local ssh_changes=0
263+
local summary=()
262264

263265
# ==================== SSH Hardening ====================
264266
info "=== SSH Configuration Hardening ==="
265267
echo ""
266268

267-
# Show current SSH settings
268-
local current_port=$(get_ssh_config "Port")
269-
local current_permit_root=$(get_ssh_config "PermitRootLogin")
270-
local current_password_auth=$(get_ssh_config "PasswordAuthentication")
269+
local current_port current_permit_root current_password_auth
270+
current_port=$(get_ssh_config "Port")
271+
current_permit_root=$(get_ssh_config "PermitRootLogin")
272+
current_password_auth=$(get_ssh_config "PasswordAuthentication")
271273

272274
echo "Current SSH Configuration:"
273-
echo " Port: ${current_port:-22} (default if not set)"
274-
echo " PermitRootLogin: ${current_permit_root:-yes} (default if not set)"
275-
echo " PasswordAuthentication: ${current_password_auth:-yes} (default if not set)"
275+
echo " Port: ${current_port:-22 (default)}"
276+
echo " PermitRootLogin: ${current_permit_root:-yes (default)}"
277+
echo " PasswordAuthentication: ${current_password_auth:-yes (default)}"
276278
echo ""
277279

278-
# Ask if user wants to harden SSH
279-
echo ""
280-
if gum_confirm "Would you like to configure SSH hardening?" "n"; then
280+
if prompt_yes_no "Configure SSH hardening?" "n"; then
281281
backup_path=$(backup_ssh_config)
282282
info "SSH config backed up to: $backup_path"
283283
echo ""
284284

285285
# Change SSH port
286-
if gum_confirm "Change SSH port from default (22)?" "n"; then
286+
if prompt_yes_no "Change SSH port from ${current_port:-22}?" "n"; then
287287
echo ""
288-
info "Available ports:"
289-
echo " 1) 2222 (commonly used alternative)"
288+
echo " 1) 2222 (common alternative)"
290289
echo " 2) 1022 (registered alternative)"
291290
echo " 3) Custom port"
292291
echo ""
293-
294-
local port_choice=$(gum_choose "Select new SSH port:" "2222" "1022" "Custom port")
292+
read -rp "Select option [1-3]: " port_choice
295293

296294
local new_port=""
297295
case "$port_choice" in
298-
"2222")
299-
new_port="2222"
300-
;;
301-
"1022")
302-
new_port="1022"
303-
;;
304-
"Custom port")
305-
new_port=$(gum_input "Enter custom SSH port (1024-65535):" "")
296+
1) new_port="2222" ;;
297+
2) new_port="1022" ;;
298+
3)
299+
read -rp "Enter port (1024-65535): " new_port
306300
if [[ ! "$new_port" =~ ^[0-9]+$ ]] || [ "$new_port" -lt 1024 ] || [ "$new_port" -gt 65535 ]; then
307-
warn "Invalid port number. Skipping port change."
301+
warn "Invalid port. Skipping."
308302
new_port=""
309303
fi
310304
;;
305+
*) warn "Invalid choice. Skipping port change." ;;
311306
esac
312307

313308
if [ -n "$new_port" ]; then
314-
# Check if port is already in use on the remote server
315-
local port_in_use=$(remote_exec "ss -tlnp 2>/dev/null | grep -c ':${new_port} ' || true")
309+
local port_in_use
310+
port_in_use=$(remote_exec "ss -tlnp 2>/dev/null | grep -c ':${new_port} ' || true")
316311
if [ "${port_in_use:-0}" -gt 0 ]; then
317-
warn "Port $new_port is already in use on the remote server. Skipping port change."
312+
warn "Port $new_port already in use. Skipping."
318313
new_port=""
319314
fi
320315
fi
321316

322317
if [ -n "$new_port" ]; then
323318
echo ""
324-
warn "Changing SSH port to $new_port"
319+
warn "After this change connect with: ssh -p $new_port $SSH_USER@$SSH_HOST"
320+
echo "Rollback: set 'Port $new_port' back to 'Port 22' in /etc/ssh/sshd_config"
325321
echo ""
326-
echo "⚠ IMPORTANT: Make sure to update your firewall and SSH client configuration!"
327-
echo " After this change, connect using: ssh -p $new_port $SSH_USER@$SSH_HOST"
328-
echo ""
329-
echo "Rollback: Edit /etc/ssh/sshd_config and change 'Port $new_port' to 'Port 22'"
330-
echo ""
331-
332-
if gum_confirm "Proceed with changing SSH port to $new_port?" "n"; then
322+
if prompt_yes_no "Proceed with port change to $new_port?" "n"; then
333323
apply_ssh_config "Port" "$new_port"
334324
SSH_PORT="$new_port"
335-
# Persist SSH_PORT to shipnode.conf on the server
336325
sed -i "s/^SSH_PORT=.*/SSH_PORT=${new_port}/" shipnode.conf 2>/dev/null || true
337-
((changes_made++))
338-
success "SSH port configured: $new_port"
339-
echo ""
340-
else
341-
info "SSH port change skipped."
342-
echo ""
326+
((ssh_changes++))
327+
summary+=("SSH port changed to $new_port")
343328
fi
344329
fi
345330
fi
346331

347332
# Disable root login
348-
if gum_confirm "Disable root login via SSH?" "n"; then
349-
echo ""
350-
warn "This will prevent direct root SSH access."
351-
echo "Rollback: Edit /etc/ssh/sshd_config and change 'PermitRootLogin no' to 'PermitRootLogin yes'"
352-
echo ""
353-
354-
if gum_confirm "Proceed with disabling root login?" "n"; then
333+
if prompt_yes_no "Disable root login via SSH?" "n"; then
334+
echo "Rollback: set 'PermitRootLogin no' back to 'yes' in /etc/ssh/sshd_config"
335+
if prompt_yes_no "Proceed?" "n"; then
355336
apply_ssh_config "PermitRootLogin" "no"
356-
((changes_made++))
357-
success "Root login disabled via SSH"
358-
echo ""
359-
else
360-
info "Root login change skipped."
361-
echo ""
337+
((ssh_changes++))
338+
summary+=("Root login disabled")
362339
fi
363340
fi
364341

365342
# Disable password authentication
366-
if gum_confirm "Disable password authentication (key-based auth only)?" "n"; then
367-
echo ""
368-
warn "This will disable password authentication."
369-
echo "⚠ WARNING: Make sure you have SSH key access configured before proceeding!"
370-
echo ""
371-
echo "Rollback: Edit /etc/ssh/sshd_config and change 'PasswordAuthentication no' to 'PasswordAuthentication yes'"
372-
echo ""
373-
374-
if gum_confirm "Are you sure? You must have SSH key access!" "n"; then
343+
if prompt_yes_no "Disable password authentication (key-based only)?" "n"; then
344+
warn "Ensure SSH key access is configured before proceeding!"
345+
echo "Rollback: set 'PasswordAuthentication no' back to 'yes' in /etc/ssh/sshd_config"
346+
if prompt_yes_no "Proceed? (you must have SSH key access)" "n"; then
375347
apply_ssh_config "PasswordAuthentication" "no"
376-
((changes_made++))
377-
success "Password authentication disabled"
378-
echo ""
379-
else
380-
info "Password authentication change skipped."
381-
echo ""
348+
((ssh_changes++))
349+
summary+=("Password authentication disabled")
382350
fi
383351
fi
384352

385-
# Restart SSH if changes were made
386-
if [ $changes_made -gt 0 ]; then
353+
if [ "$ssh_changes" -gt 0 ]; then
387354
echo ""
388-
info "Restarting SSH service to apply changes..."
355+
info "Restarting SSH service..."
389356
restart_ssh_service
390357
success "SSH service restarted"
391-
echo ""
358+
((changes_made += ssh_changes))
392359
fi
393-
else
394-
info "SSH hardening skipped."
395-
echo ""
396360
fi
361+
echo ""
397362

398363
# ==================== Firewall Configuration ====================
399364
info "=== Firewall Configuration (UFW) ==="
400365
echo ""
401366

402-
local ufw_status=$(check_ufw)
367+
local ufw_status
368+
ufw_status=$(check_ufw)
403369
if [[ "$ufw_status" == "UFW_NOT_INSTALLED" ]]; then
404-
info "UFW (Uncomplicated Firewall) is not installed."
405-
if gum_confirm "Install and configure UFW firewall?" "y"; then
406-
echo ""
370+
info "UFW is not installed."
371+
if prompt_yes_no "Install UFW?" "y"; then
407372
info "Installing UFW..."
408373
install_ufw
409374
success "UFW installed"
410-
echo ""
411-
else
412-
warn "Firewall configuration skipped."
413-
echo ""
375+
summary+=("UFW installed")
414376
fi
415377
else
416378
echo "Current UFW status:"
417379
echo "$ufw_status"
418-
echo ""
419380
fi
381+
echo ""
420382

421-
# Check UFW again after potential install
422383
ufw_status=$(check_ufw)
423384
if [[ "$ufw_status" != "UFW_NOT_INSTALLED" ]]; then
424-
if gum_confirm "Configure UFW firewall rules (SSH, HTTP, HTTPS)?" "y"; then
425-
echo ""
426-
echo "Firewall rules to be applied:"
427-
echo " ✓ Allow SSH (port ${SSH_PORT:-22})"
428-
echo " ✓ Allow HTTP (port 80)"
429-
echo " ✓ Allow HTTPS (port 443)"
430-
echo " ✗ Deny all other incoming traffic"
431-
echo ""
432-
echo "Rollback: Run 'sudo ufw disable' or 'sudo ufw reset'"
433-
echo ""
434-
435-
if gum_confirm "Proceed with firewall configuration?" "y"; then
436-
apply_firewall_rules "${SSH_PORT:-22}"
437-
((changes_made++))
438-
success "Firewall configured successfully"
439-
echo ""
440-
info "Current status:"
441-
check_ufw
442-
echo ""
443-
else
444-
info "Firewall configuration skipped."
445-
echo ""
446-
fi
447-
else
448-
info "Firewall configuration skipped."
385+
echo "Rules to apply: SSH (${SSH_PORT:-22}), HTTP (80), HTTPS (443) — deny everything else"
386+
echo "Rollback: sudo ufw disable"
387+
if prompt_yes_no "Configure UFW rules?" "y"; then
388+
apply_firewall_rules "${SSH_PORT:-22}"
389+
((changes_made++))
390+
summary+=("UFW configured: SSH/HTTP/HTTPS allowed, all else denied")
391+
success "Firewall configured"
449392
echo ""
393+
check_ufw
450394
fi
451395
fi
396+
echo ""
452397

453398
# ==================== Fail2ban Configuration ====================
454399
info "=== Fail2ban (Intrusion Prevention) ==="
455400
echo ""
456401

457-
local fail2ban_status=$(check_fail2ban)
402+
local fail2ban_status
403+
fail2ban_status=$(check_fail2ban)
458404
case "$fail2ban_status" in
459405
"ACTIVE")
460-
info "Fail2ban is already installed and active"
461-
echo ""
406+
success "Fail2ban already installed and active"
462407
;;
463408
"INSTALLED_INACTIVE")
464-
warn "Fail2ban is installed but not running"
465-
if gum_confirm "Start and enable fail2ban?" "y"; then
409+
warn "Fail2ban installed but not running"
410+
if prompt_yes_no "Start and enable fail2ban?" "y"; then
466411
remote_exec "sudo systemctl start fail2ban && sudo systemctl enable fail2ban"
467412
success "Fail2ban started and enabled"
468413
((changes_made++))
414+
summary+=("Fail2ban started and enabled")
469415
fi
470-
echo ""
471416
;;
472417
"NOT_INSTALLED")
473-
info "Fail2ban is not installed."
474-
echo "Fail2ban monitors log files and bans IPs that show malicious signs (e.g., multiple failed SSH attempts)."
475-
echo ""
476-
477-
if gum_confirm "Install and configure fail2ban?" "n"; then
478-
echo ""
418+
info "Fail2ban not installed."
419+
echo "Monitors logs and bans IPs after repeated failed SSH attempts."
420+
echo "Config: 5 retries / 10 min window / 1 hour ban"
421+
echo "Rollback: sudo systemctl stop fail2ban && sudo systemctl disable fail2ban"
422+
if prompt_yes_no "Install and configure fail2ban?" "n"; then
479423
info "Installing fail2ban..."
480424
install_configure_fail2ban
481425
success "Fail2ban installed and configured"
482-
echo ""
483-
echo "Configuration:"
484-
echo " - Max retry: 5 attempts"
485-
echo " - Find time: 10 minutes"
486-
echo " - Ban time: 1 hour"
487-
echo ""
488-
echo "Rollback: sudo systemctl stop fail2ban && sudo systemctl disable fail2ban"
489-
echo " Then remove the package using your system's package manager"
490-
echo ""
491426
((changes_made++))
492-
else
493-
info "Fail2ban installation skipped."
494-
echo ""
427+
summary+=("Fail2ban installed (5 retries / 10 min / 1 hr ban)")
495428
fi
496429
;;
497430
esac
431+
echo ""
498432

499433
# ==================== Summary ====================
500-
echo ""
501434
info "=== Security Hardening Summary ==="
502435
echo ""
503436

504-
if [ $changes_made -eq 0 ]; then
505-
warn "No security changes were made."
506-
info "Server configuration remains unchanged."
437+
if [ ${#summary[@]} -eq 0 ]; then
438+
warn "No changes made. Server configuration unchanged."
507439
else
508-
success "Security hardening completed!"
509-
info "Total changes applied: $changes_made"
440+
success "Changes applied: ${#summary[@]}"
441+
echo ""
442+
for item in "${summary[@]}"; do
443+
echo "$item"
444+
done
510445
echo ""
511446

512-
if [ -n "$backup_path" ]; then
513-
info "SSH configuration backup: $backup_path"
514-
fi
447+
[ -n "$backup_path" ] && info "SSH config backup: $backup_path"
515448

516449
echo ""
517-
echo "Important reminders:"
518-
echo " - Test your SSH connection before closing this session"
519-
echo " - If you changed the SSH port, update your SSH client config"
520-
echo " - Review firewall rules with: sudo ufw status"
521-
echo " - Check fail2ban status with: sudo fail2ban-client status"
450+
echo "Reminders:"
451+
echo " - Test SSH connection before closing this session"
452+
echo " - Review firewall: sudo ufw status"
453+
echo " - Check fail2ban: sudo fail2ban-client status"
522454
echo ""
523455

524-
# Test SSH connection
525456
info "Testing SSH connection..."
526457
if remote_exec "exit" &>/dev/null; then
527458
success "SSH connection verified"
528459
else
529-
warn "SSH connection test failed!"
530-
warn "Please verify your connection settings before disconnecting."
460+
warn "SSH connection test failed — verify settings before disconnecting!"
531461
fi
532462
fi
533463

lib/core.sh

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ BLUE='\033[0;34m'
1010
NC='\033[0m' # No Color
1111

1212
# ShipNode version
13-
VERSION="1.4.12"
13+
VERSION="1.4.13"
1414

1515
# SSH multiplexing for connection reuse
1616
SSH_CONTROL_PATH="/tmp/shipnode-ssh-%r@%h:%p"

0 commit comments

Comments
 (0)