Post

Estação de Trabalho como Código (Parte 4.5): Menu SSH Interativo

Aprenda a usar menu-ssh, um gerenciador interativo de hosts SSH baseado em fzf. Este tutorial complementar mostra como instalar, configurar e usar a ferramenta para gerenciar múltiplos hosts SSH de forma visual e intuitiva, com suporte completo a ProxyJump e estrutura modular.

Estação de Trabalho como Código (Parte 4.5): Menu SSH Interativo

Tutorial Anterior: Estação de Trabalho como Código (Parte 4): Organizando Acessos com SSH Config

Introdução

Na Parte 4, aprendemos a criar uma estrutura modular e profissional para gerenciar hosts SSH. Embora a edição manual de arquivos seja poderosa e ofereça controle total, pode ser tedioso quando você tem muitos hosts ou precisa adicionar/remover hosts frequentemente.

Para melhorar a experiência, criamos menu-ssh, um script Bash interativo que oferece uma interface visual baseada em fzf (que você já instalou na Parte 1) para gerenciar hosts SSH de forma intuitiva e segura. O menu-ssh é totalmente integrado com a estrutura modular que criamos, respeitando os arquivos config.d/pessoal.conf, config.d/trabalho.conf e config.d/lab.conf.

Observação: Esta é uma parte complementar e opcional. Se você preferir gerenciar SSH Config manualmente, pode pular para a Parte 5. No entanto, recomendamos usar menu-ssh para melhorar sua produtividade.

Objetivos desta Parte

  • Entender o que é menu-ssh e seus benefícios
  • Instalar o script menu-ssh no seu ambiente
  • Usar menu-ssh para adicionar, remover e listar hosts
  • Testar conexões SSH via menu-ssh
  • Conectar a hosts usando seleção visual
  • Entender como menu-ssh integra com a estrutura modular
  • Resolver problemas comuns com troubleshooting

Pré-requisitos

  • Conclusão da Parte 4 desta série
  • fzf instalado (foi instalado na Parte 1)
  • Conhecimento básico de SSH Config (coberto na Parte 4)

A quem se destina

Este tutorial é ideal para:

  • SysAdmins: Que querem uma interface visual para gerenciar hosts
  • DevOps Engineers: Que usam SSH diariamente e querem mais produtividade
  • Desenvolvedores: Que trabalham com múltiplos servidores
  • Profissionais de TI: Que querem automatizar tarefas comuns

Pré-conhecimento: Nenhum pré-conhecimento adicional é necessário além da Parte 4.

Tempo Estimado

30-40 minutos

Isso inclui:

  • Leitura e compreensão: ~10 min
  • Instalação: ~5 min
  • Testes e exploração: ~10-15 min
  • Troubleshooting (se necessário): ~5-10 min

Dica Útil: Se você quiser apenas instalar e usar, pode fazer em 15 minutos. Mas recomendamos ler e entender cada seção.


O que é menu-ssh?

menu-ssh é um script Bash interativo que oferece:

  • Interface visual com fzf para seleção de hosts
  • Adicionar novos hosts com validação completa
  • Remover hosts com confirmação e backup
  • Listar todos os hosts com informações formatadas
  • Testar conexões SSH
  • Conectar a hosts via seleção visual
  • Suporte completo a ProxyJump
  • Seleção visual de chaves SSH
  • Integração com estrutura modular (config.d/)

Por que usar menu-ssh?

AspectoBenefício
Interface AmigávelFácil de usar, mesmo para iniciantes
Menos ErrosValidação de entrada em cada passo
OrganizaçãoCategoriza hosts (pessoal, trabalho, lab)
ProdutividadeConectar a hosts com poucos cliques
ProfissionalCores, formatação e feedback claro
SeguroBackups automáticos antes de modificações
ModularIntegrado com estrutura config.d/

Instalando o menu-ssh

Passo 1: Criar o Diretório

1
2
3
4
5
6
7
8
# Crie o diretório para o script
$ mkdir -p ~/workspace-as-code/scripts/menu-ssh

# Verifique se foi criado
$ ls -la ~/workspace-as-code/scripts/

# Você deve ver:
# drwxr-xr-x menu-ssh

Passo 2: Copiar o Script

Copie o script menu-ssh para o diretório. O script completo está abaixo:

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
$ cat > ~/workspace-as-code/scripts/menu-ssh/menu-ssh << 'EOF'
#!/bin/bash

################################################################################
# menu-ssh - Gerenciador Interativo de Conexões SSH
################################################################################
#
# Descrição:
#   Script interativo para gerenciar, listar e conectar a hosts SSH configurados.
#   Utiliza fzf para uma interface de seleção intuitiva e oferece funcionalidades
#   como adicionar, remover, listar e testar conexões SSH.
#   Adaptado para trabalhar com estrutura modular de SSH Config (config.d/).
#
# Dependências:
#   - bash (versão 4.0+)
#   - fzf (fuzzy finder) - instalado na Parte 1
#   - ssh (OpenSSH)
#
# Uso:
#   menu-ssh
#
# Arquivos de Configuração:
#   ~/.ssh/config                  - Configuração principal (Include)
#   ~/.ssh/config.d/pessoal.conf   - Hosts pessoais
#   ~/.ssh/config.d/trabalho.conf  - Hosts de trabalho
#   ~/.ssh/config.d/lab.conf       - Hosts de laboratório
#
# Autor: Gean Martins (adaptado para workspace-as-code)
# Versão: 2.0.0
# Data: 2026-02-19
#
################################################################################

set -euo pipefail

# ============================================================================
# Definições de Cores
# ============================================================================

readonly RED='\033[0;31m'
readonly GREEN='\033[0;32m'
readonly YELLOW='\033[1;33m'
readonly BLUE='\033[0;34m'
readonly CYAN='\033[0;36m'
readonly MAGENTA='\033[0;35m'
readonly NC='\033[0m' # No Color

# ============================================================================
# Variáveis Globais
# ============================================================================

readonly SSH_CONFIG="${HOME}/.ssh/config"
readonly SSH_CONFIG_DIR="${HOME}/.ssh/config.d"
readonly CATEGORIES=("pessoal" "trabalho" "lab")

# ============================================================================
# Funções Auxiliares
# ============================================================================

die() {
    echo -e "${RED}Erro: $*${NC}" >&2
    exit 1
}

warn() {
    echo -e "${YELLOW}Aviso: $*${NC}" >&2
}

success() {
    echo -e "${GREEN}$*${NC}"
}

info() {
    echo -e "${CYAN}$*${NC}"
}

command_exists() {
    command -v "$1" &>/dev/null
}

check_fzf() {
    if ! command_exists fzf; then
        die "fzf não está instalado. Instale com: sudo apt install fzf"
    fi
}

initialize_ssh_environment() {
    mkdir -p "$HOME/.ssh" "$SSH_CONFIG_DIR"
    touch "$SSH_CONFIG"
    chmod 700 "$HOME/.ssh"
    chmod 600 "$SSH_CONFIG"
    
    for category in "${CATEGORIES[@]}"; do
        local config_file="$SSH_CONFIG_DIR/${category}.conf"
        if [[ ! -f "$config_file" ]]; then
            touch "$config_file"
            chmod 600 "$config_file"
        fi
    done
}

# ============================================================================
# Funções de Backup
# ============================================================================

backup_ssh_config() {
    local config_file=$1
    local backup_file="${config_file}.bak.$(date +%Y%m%d_%H%M%S)"
    cp "$config_file" "$backup_file"
    info "Backup criado: $backup_file"
}

# ============================================================================
# Funções de Validação
# ============================================================================

validate_ip() {
    local ip=$1
    local stat=1
    
    if [[ $ip =~ ^[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}$ ]]; then
        local OIFS=$IFS
        IFS='.'
        local ip_array=($ip)
        IFS=$OIFS
        [[ ${ip_array[0]} -le 255 && ${ip_array[1]} -le 255 && \
           ${ip_array[2]} -le 255 && ${ip_array[3]} -le 255 ]]
        stat=$?
    fi
    return $stat
}

validate_hostname() {
    local hostname=$1
    [[ "$hostname" =~ ^[a-zA-Z0-9.-]+$ ]]
}

validate_alias() {
    local alias=$1
    [[ "$alias" =~ ^[a-zA-Z0-9_.-]+$ ]]
}

validate_port() {
    local port=$1
    [[ $port =~ ^[0-9]+$ ]] && (( port >= 1 && port <= 65535 ))
}

# ============================================================================
# Funções de Leitura de Hosts
# ============================================================================

get_all_hosts() {
    grep -h "^Host " "$SSH_CONFIG" "$SSH_CONFIG_DIR"/*.conf 2>/dev/null | \
        awk '{print $2}' | grep -v "^\*$" | sort -u
}

get_host_info() {
    local alias=$1
    awk -v host="$alias" '
        $1 == "Host" && $2 == host { found=1; next }
        found && /^Host / { exit }
        found { print }
    ' "$SSH_CONFIG" "$SSH_CONFIG_DIR"/*.conf 2>/dev/null
}

get_host_config_file() {
    local alias=$1
    
    for config_file in "$SSH_CONFIG" "$SSH_CONFIG_DIR"/*.conf; do
        if [[ -f "$config_file" ]] && grep -q "^Host $alias$" "$config_file"; then
            echo "$config_file"
            return 0
        fi
    done
    
    return 1
}

list_hosts_formatted() {
    local all_hosts
    all_hosts=$(get_all_hosts)
    
    if [[ -z "$all_hosts" ]]; then
        warn "Nenhum host encontrado na configuração SSH."
        return 1
    fi
    
    info "--- Hosts SSH Configurados ---"
    echo ""
    printf "${BLUE}%-20s %-15s %-8s %-15s %s${NC}\n" "ALIAS" "HOSTNAME" "PORT" "USER" "ARQUIVO"
    echo "------------------------------------------------------------------------------------"
    
    while IFS= read -r alias; do
        local config_file
        config_file=$(get_host_config_file "$alias")
        local config_name=$(basename "$config_file" .conf)
        
        local hostname=$(get_host_info "$alias" | grep "HostName" | awk '{print $2}' | head -1)
        local port=$(get_host_info "$alias" | grep "Port" | awk '{print $2}' | head -1)
        local user=$(get_host_info "$alias" | grep "User" | awk '{print $2}' | head -1)
        
        hostname=${hostname:-"N/A"}
        port=${port:-"22"}
        user=${user:-"N/A"}
        
        printf "%-20s %-15s %-8s %-15s %s\n" "$alias" "$hostname" "$port" "$user" "$config_name"
    done <<< "$all_hosts"
    
    echo ""
}

# ============================================================================
# Funções de Gerenciamento de Hosts
# ============================================================================

detect_category() {
    info "Selecione a categoria para este host:"
    echo ""
    
    local category
    category=$(printf "%s\n" "${CATEGORIES[@]}" | fzf --prompt="Categoria > " --height="10%")
    
    if [[ -z "$category" ]]; then
        die "Categoria não selecionada."
    fi
    
    echo "$category"
}

list_available_keys() {
    find "$HOME/.ssh" -maxdepth 1 -type f \( -name "id_*" -o -name "*_key" \) ! -name "*.pub" 2>/dev/null | sort
}

select_ssh_key() {
    local available_keys
    available_keys=$(list_available_keys)
    
    if [[ -z "$available_keys" ]]; then
        warn "Nenhuma chave SSH encontrada em ~/.ssh"
        echo "$HOME/.ssh/id_rsa"
        return 0
    fi
    
    info "Selecione a chave SSH (ou Enter para usar padrão):"
    echo ""
    
    local selected_key
    selected_key=$(echo "$available_keys" | fzf --prompt="Chave SSH > " --height="15%")
    
    if [[ -z "$selected_key" ]]; then
        echo "$HOME/.ssh/id_rsa"
    else
        echo "$selected_key"
    fi
}

select_proxyjump() {
    local all_hosts
    all_hosts=$(get_all_hosts)
    
    if [[ -z "$all_hosts" ]]; then
        return 0
    fi
    
    info "Selecione um ProxyJump (ou Enter para pular):"
    echo ""
    
    local proxyjump
    proxyjump=$(echo "$all_hosts" | fzf --prompt="ProxyJump > " --height="15%")
    
    echo "$proxyjump"
}

add_ssh() {
    info "--- Adicionar Novo Host SSH ---"
    echo ""
    
    read -rp "Digite o alias do host (ex: vm-01): " alias
    if [[ -z "$alias" ]]; then
        die "O alias não pode ser vazio."
    fi
    
    if ! validate_alias "$alias"; then
        die "O alias deve conter apenas letras, números, pontos, hífens e underscores."
    fi
    
    if grep -q -E "^\s*Host\s+${alias}\s*$" "$SSH_CONFIG" "$SSH_CONFIG_DIR"/*.conf 2>/dev/null; then
        die "O alias '$alias' já existe no arquivo de configuração SSH."
    fi
    
    read -rp "Digite o IP ou Hostname do host (ex: 192.168.200.150): " ip
    if [[ -z "$ip" ]]; then
        die "IP/Hostname não pode ser vazio."
    fi
    
    if ! validate_ip "$ip" && ! validate_hostname "$ip"; then
        die "IP ou hostname inválido."
    fi
    
    read -rp "Digite a porta SSH (padrão: 22): " port
    port=${port:-22}
    
    if ! validate_port "$port"; then
        die "Porta inválida. Deve ser um número entre 1 e 65535."
    fi
    
    read -rp "Digite o usuário (padrão: $USER): " user
    user=${user:-$USER}
    
    local key
    key=$(select_ssh_key)
    
    local proxyjump
    proxyjump=$(select_proxyjump)
    
    read -rp "ForwardAgent (y/N): " forward_agent
    
    local category
    category=$(detect_category)
    
    local config_file="$SSH_CONFIG_DIR/${category}.conf"
    
    if [[ -n "$key" && ! -f "$key" ]]; then
        warn "O arquivo de chave '$key' não existe."
        read -rp "Deseja continuar? (s/N): " confirm
        if [[ ! "$confirm" =~ ^[sS]([iI][mM])?$ ]]; then
            echo "Cancelado."
            return 1
        fi
    fi
    
    backup_ssh_config "$config_file"
    
    {
        echo ""
        echo "Host $alias"
        echo "    HostName $ip"
        echo "    Port $port"
        echo "    User $user"
        [[ -n "$key" ]] && echo "    IdentityFile $key"
        [[ -n "$proxyjump" ]] && echo "    ProxyJump $proxyjump"
        [[ "$forward_agent" =~ ^[yY] ]] && echo "    ForwardAgent yes"
    } >> "$config_file"
    
    success "Host '$alias' adicionado com sucesso em $category.conf!"
}

remove_ssh() {
    info "--- Remover Host SSH ---"
    echo ""
    
    local all_hosts
    all_hosts=$(get_all_hosts)
    
    if [[ -z "$all_hosts" ]]; then
        warn "Nenhum host para remover."
        return 1
    fi
    
    local host_to_remove
    host_to_remove=$(echo "$all_hosts" | fzf --prompt="Selecione o host para REMOVER: " --height="15%")
    
    if [[ -z "$host_to_remove" ]]; then
        echo "Remoção cancelada."
        return 0
    fi
    
    local config_file
    config_file=$(get_host_config_file "$host_to_remove")
    
    warn "Você está prestes a remover o host '$host_to_remove'."
    warn "A seguinte entrada será removida de $(basename "$config_file"):"
    echo "----------------------------------------------------"
    get_host_info "$host_to_remove"
    echo "----------------------------------------------------"
    echo ""
    
    read -rp "Tem certeza que deseja continuar? (s/N): " confirm
    
    if [[ "$confirm" =~ ^[sS]([iI][mM])?$ ]]; then
        backup_ssh_config "$config_file"
        
        sed -i "/^Host $host_to_remove$/,/^$/d" "$config_file"
        
        success "Host '$host_to_remove' removido com sucesso."
    else
        echo "Remoção cancelada pelo usuário."
    fi
}

test_ssh() {
    info "--- Testar Conexão SSH ---"
    echo ""
    
    local all_hosts
    all_hosts=$(get_all_hosts)
    
    if [[ -z "$all_hosts" ]]; then
        warn "Nenhum host para testar."
        return 1
    fi
    
    local host_to_test
    host_to_test=$(echo "$all_hosts" | fzf --prompt="Selecione o host para TESTAR: " --height="15%")
    
    if [[ -z "$host_to_test" ]]; then
        return 0
    fi
    
    echo ""
    echo -e "${YELLOW}Testando conexão com $host_to_test...${NC}"
    echo ""
    
    if ssh -q -o BatchMode=yes -o ConnectTimeout=5 "$host_to_test" exit 2>/dev/null; then
        success "Conexão bem-sucedida com $host_to_test"
    else
        echo -e "${RED}Falha na conexão com $host_to_test${NC}"
        echo ""
        warn "Dica: Use 'ssh -v $host_to_test' para mais detalhes"
    fi
}

connect_ssh() {
    local alias=$1
    success "Conectando ao host '$alias'..."
    echo ""
    ssh "$alias"
}

# ============================================================================
# Menu Principal
# ============================================================================

main() {
    check_fzf
    
    initialize_ssh_environment
    
    local choice
    choice=$( (echo "Adicionar novo host"; echo "Remover host"; echo "Listar hosts"; echo "Testar conexão"; echo "---"; get_all_hosts) | fzf --prompt="SSH Menu > " --height="40%" --border )
    
    case "$choice" in
        "")
            echo "Nenhuma ação selecionada."
            ;;
        "Adicionar novo host")
            add_ssh
            ;;
        "Remover host")
            remove_ssh
            ;;
        "Listar hosts")
            list_hosts_formatted
            ;;
        "Testar conexão")
            test_ssh
            ;;
        "---")
            echo "Nenhuma ação selecionada."
            ;;
        *)
            if [[ -n "$choice" ]]; then
                connect_ssh "$choice"
            else
                die "Opção inválida selecionada."
            fi
            ;;
    esac
}

main "$@"
EOF

# Dê permissão de execução
$ chmod +x ~/workspace-as-code/scripts/menu-ssh/menu-ssh

# Verifique se foi criado
$ ls -la ~/workspace-as-code/scripts/menu-ssh/

# Você deve ver:
# -rwxr-xr-x menu-ssh

Para usar o script de qualquer lugar, crie um link simbólico em /usr/local/bin:

1
2
3
4
5
6
7
8
9
10
11
# Crie o link simbólico
$ sudo ln -s ~/workspace-as-code/scripts/menu-ssh/menu-ssh /usr/local/bin/menu-ssh

# Verifique se foi criado
$ ls -la /usr/local/bin/menu-ssh

# Você deve ver:
# lrwxrwxrwx menu-ssh -> /home/user/workspace-as-code/scripts/menu-ssh/menu-ssh

# Teste se funciona
$ menu-ssh

Informação: O link simbólico permite chamar menu-ssh de qualquer diretório sem precisar do caminho completo.

Passo 4: Adicionar ao setup_symlinks.sh (Opcional)

Se quiser automatizar a criação do link simbólico, adicione ao script setup_symlinks.sh:

1
2
3
4
5
6
7
8
9
# Edite o script
$ vim ~/workspace-as-code/scripts/setup_symlinks.sh

# Adicione após a seção de symlinks:
# Cria link para menu-ssh
if [ -f ~/workspace-as-code/scripts/menu-ssh/menu-ssh ]; then
    sudo ln -sf ~/workspace-as-code/scripts/menu-ssh/menu-ssh /usr/local/bin/menu-ssh
    success "Link criado para menu-ssh"
fi

Usando o menu-ssh

Iniciando o Menu

1
2
3
4
# Execute o menu-ssh
$ menu-ssh

# Você verá a tela principal com opções

Estrutura do Menu

O menu-ssh oferece as seguintes opções:

OpçãoFunção
Adicionar novo hostAdiciona um novo host SSH interativamente
Remover hostRemove um host existente com confirmação
Listar hostsMostra todos os hosts com informações
Testar conexãoTesta a conexão com um host
[Hosts]Conecta diretamente a um host

Exemplos de Uso

Exemplo 1: Adicionar um Novo Host Pessoal

1
2
3
4
5
6
7
8
9
10
11
12
13
14
$ menu-ssh

# Selecione "Adicionar novo host"
# Digite o alias: meu-vps
# Digite o IP: 203.0.113.42
# Digite a porta: 2222
# Digite o usuário: ubuntu
# Selecione a chave: ~/.ssh/vps_key
# Selecione ProxyJump: (Enter para pular)
# ForwardAgent: N
# Selecione a categoria: pessoal

# Resultado:
# Host 'meu-vps' adicionado com sucesso em pessoal.conf!

Exemplo 2: Adicionar um Host com ProxyJump

1
2
3
4
5
6
7
8
9
10
11
12
13
14
$ menu-ssh

# Selecione "Adicionar novo host"
# Digite o alias: servidor-interno
# Digite o IP: 10.10.1.50
# Digite a porta: 22
# Digite o usuário: admin
# Selecione a chave: ~/.ssh/trabalho_key
# Selecione ProxyJump: jump.trabalho
# ForwardAgent: N
# Selecione a categoria: trabalho

# Resultado:
# Host 'servidor-interno' adicionado com sucesso em trabalho.conf!

Exemplo 3: Listar Todos os Hosts

1
2
3
4
5
6
7
8
9
10
11
12
13
$ menu-ssh

# Selecione "Listar hosts"

# Você verá algo como:
# --- Hosts SSH Configurados ---
#
# ALIAS                HOSTNAME        PORT     USER            ARQUIVO
# ------------------------------------------------------------------------------------
# meu-vps              203.0.113.42    2222     ubuntu          pessoal
# servidor-interno     10.10.1.50      22       admin           trabalho
# jump.trabalho        203.0.113.5     22       seu_usuario     trabalho
# lab-vm1              192.168.1.100   22       ubuntu          lab

Exemplo 4: Testar Conexão

1
2
3
4
5
6
7
8
9
$ menu-ssh

# Selecione "Testar conexão"
# Selecione o host: meu-vps

# Você verá:
# Testando conexão com meu-vps...
#
# Conexão bem-sucedida com meu-vps

Exemplo 5: Conectar a um Host

1
2
3
4
5
6
7
8
$ menu-ssh

# Você verá todos os hosts listados
# Selecione: servidor-interno

# Resultado:
# Conectando ao host 'servidor-interno'...
# [Conecta ao servidor via ProxyJump automaticamente]

Exemplo 6: Remover um Host

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
$ menu-ssh

# Selecione "Remover host"
# Selecione o host: meu-vps

# Você verá:
# Aviso: Você está prestes a remover o host 'meu-vps'.
# A seguinte entrada será removida de pessoal.conf:
# Host meu-vps
#     HostName 203.0.113.42
#     Port 2222
#     User ubuntu
#     IdentityFile ~/.ssh/vps_key
# Tem certeza que deseja continuar? (s/N): s

# Resultado:
# Host 'meu-vps' removido com sucesso.

Entendendo o Script menu-ssh

O script é bem estruturado e segue boas práticas. Vamos entender as principais seções:

Seção 1: Inicialização

1
set -euo pipefail

Garante que o script falhe se houver erro, variável indefinida ou falha em pipe.

Seção 2: Cores e Formatação

1
2
3
readonly RED='\033[0;31m'
readonly GREEN='\033[0;32m'
# ... etc

Define cores para tornar a saída mais legível.

Seção 3: Funções Auxiliares

1
2
3
4
die()      # Exibe erro e sai
warn()     # Exibe aviso
success()  # Exibe sucesso
info()     # Exibe informação

Funções para padronizar mensagens.

Seção 4: Leitura de Hosts

1
2
3
get_all_hosts()           # Extrai todos os hosts
get_host_info()           # Extrai informações de um host
get_host_config_file()    # Encontra o arquivo onde o host está

Funções para ler a configuração SSH modular.

Seção 5: Gerenciamento de Hosts

1
2
3
4
add_ssh()      # Adiciona novo host
remove_ssh()   # Remove host
test_ssh()     # Testa conexão
connect_ssh()  # Conecta a host

Funções principais do script.

Seção 6: Menu Principal

1
main()

Orquestra o fluxo do programa.


Recursos Avançados

Seleção Visual de Chaves SSH

Quando você adiciona um novo host, o script lista todas as chaves SSH disponíveis em ~/.ssh:

1
2
3
4
5
# Selecione a chave SSH (ou Enter para usar padrão):
# ~/.ssh/id_rsa
# ~/.ssh/github_key
# ~/.ssh/trabalho_key
# ~/.ssh/vps_key

Basta usar as setas para navegar e Enter para selecionar.

Seleção de ProxyJump

O script lista todos os hosts já configurados para seleção como ProxyJump:

1
2
3
4
# Selecione um ProxyJump (ou Enter para pular):
# jump.trabalho
# jump.interno
# bastion.empresa

Isso garante que você não digite um host inexistente.

Categorização Automática

Ao adicionar um host, o script pede para selecionar a categoria:

1
2
3
4
# Selecione a categoria para este host:
# pessoal
# trabalho
# lab

O host será adicionado ao arquivo correto (pessoal.conf, trabalho.conf ou lab.conf).

Backups Automáticos

Antes de modificar qualquer arquivo, o script cria um backup:

1
# Backup criado: /home/user/.ssh/config.d/pessoal.conf.bak.20260219_143022

Se algo der errado, você pode restaurar o backup.


Troubleshooting

Erro: “fzf não está instalado”

Problema: Você recebe esse erro ao executar menu-ssh.

Solução: fzf foi instalado na Parte 1. Se não estiver, instale:

1
2
3
4
$ sudo apt install fzf

# Verifique
$ fzf --version

Erro: “Permission denied” ao executar menu-ssh

Problema: O script não tem permissão de execução.

Solução:

1
2
3
4
5
6
7
# Dê permissão de execução
$ chmod +x ~/workspace-as-code/scripts/menu-ssh/menu-ssh

# Verifique
$ ls -la ~/workspace-as-code/scripts/menu-ssh/menu-ssh

# Deve ser: -rwxr-xr-x

Erro: “command not found: menu-ssh”

Problema: O link simbólico não foi criado ou está quebrado.

Solução:

1
2
3
4
5
6
7
8
9
# Verifique se o link existe
$ ls -la /usr/local/bin/menu-ssh

# Se não existir, crie
$ sudo ln -s ~/workspace-as-code/scripts/menu-ssh/menu-ssh /usr/local/bin/menu-ssh

# Se estiver quebrado, recrie
$ sudo rm /usr/local/bin/menu-ssh
$ sudo ln -s ~/workspace-as-code/scripts/menu-ssh/menu-ssh /usr/local/bin/menu-ssh

Erro: “Host already exists”

Problema: Você tenta adicionar um host que já existe.

Solução:

1
2
3
4
5
6
7
8
9
# Verifique se o host existe
$ grep "^Host seu-host$" ~/.ssh/config ~/.ssh/config.d/*

# Se existir, remova-o primeiro
$ menu-ssh
# Selecione "Remover host"
# Selecione o host que deseja remover

# Depois adicione novamente

Erro: “Invalid IP or hostname”

Problema: Você digitou um IP ou hostname inválido.

Solução:

1
2
3
4
# IPs válidos: 192.168.1.1, 203.0.113.42
# Hostnames válidos: example.com, server.example.com, my-server

# Tente novamente com um IP ou hostname válido

Erro: “Port invalid”

Problema: Você digitou uma porta inválida.

Solução:

1
2
3
4
# Portas válidas: 1-65535
# Porta padrão SSH: 22

# Tente novamente com um número entre 1 e 65535

Hosts não aparecem no menu

Problema: Você adicionou hosts, mas não aparecem no menu.

Solução:

1
2
3
4
5
6
7
8
9
10
11
12
# Verifique se os arquivos de configuração existem
$ ls -la ~/.ssh/config.d/

# Você deve ver:
# -rw------- pessoal.conf
# -rw------- trabalho.conf
# -rw------- lab.conf

# Verifique se há hosts nos arquivos
$ cat ~/.ssh/config.d/pessoal.conf

# Se estiverem vazios, adicione hosts via menu-ssh

Conexão falha ao testar

Problema: O teste de conexão falha.

Solução:

1
2
3
4
5
6
7
8
9
10
11
12
# Teste com verbose para mais detalhes
$ ssh -v seu-host

# Verifique:
# 1. Se o host está online
# 2. Se a chave SSH está correta
# 3. Se o usuário está correto
# 4. Se a porta está correta
# 5. Se o ProxyJump está configurado corretamente

# Se usar ProxyJump, teste o jump host primeiro
$ ssh -v jump.trabalho

ProxyJump não funciona

Problema: Conexão via ProxyJump falha.

Solução:

1
2
3
4
5
6
7
8
9
10
11
12
13
# Verifique se o jump host está configurado
$ grep -A 5 "^Host jump.trabalho" ~/.ssh/config.d/trabalho.conf

# Verifique se consegue conectar ao jump host
$ ssh -v jump.trabalho

# Se funcionar, teste o servidor interno
$ ssh -v servidor-interno

# Se não funcionar, verifique:
# 1. Se o jump host está online
# 2. Se o ProxyJump está escrito corretamente
# 3. Se a chave SSH do jump host está correta

Backup não foi criado

Problema: Você não vê o arquivo de backup.

Solução:

1
2
3
4
5
6
7
8
9
10
11
# Backups são criados no mesmo diretório do arquivo original
# com sufixo .bak.TIMESTAMP

# Procure por backups
$ ls -la ~/.ssh/config.d/*.bak*

# Você deve ver algo como:
# -rw------- pessoal.conf.bak.20260219_143022

# Para restaurar um backup
$ cp ~/.ssh/config.d/pessoal.conf.bak.20260219_143022 ~/.ssh/config.d/pessoal.conf

Dicas e Boas Práticas

Dica 1: Use Categorias Consistentemente

Organize seus hosts em categorias:

  • pessoal: VPS pessoais, servidores de hobby
  • trabalho: Infraestrutura corporativa, jump hosts
  • lab: Máquinas virtuais de laboratório, testes

Dica 2: Use Nomes Descritivos

1
2
3
4
5
6
7
8
9
# Bom
vps-pessoal
jump.trabalho
db-server-prod

# Ruim
vm1
host2
servidor

Dica 3: Sempre Teste Conexões

Após adicionar um host, teste a conexão:

1
2
3
$ menu-ssh
# Selecione "Testar conexão"
# Selecione o host que acabou de adicionar

Dica 4: Mantenha Backups

O script cria backups automaticamente, mas você pode fazer backups manuais:

1
$ cp -r ~/.ssh/config.d ~/.ssh/config.d.backup

Dica 5: Use ProxyJump para Redes Internas

Se você tem servidores em uma rede interna, use ProxyJump:

1
2
3
# Ao adicionar um host interno:
# Selecione ProxyJump: jump.trabalho
# Isso permite conectar sem SSH manual para o jump host

Dica 6: Revise Configurações Periodicamente

1
2
3
4
5
# Listar todos os hosts
$ menu-ssh
# Selecione "Listar hosts"

# Remova hosts que não usa mais

Integrando com Seu Fluxo de Trabalho

Adicionar menu-ssh ao Git

1
2
3
4
5
# Adicione o script ao repositório
$ cd ~/workspace-as-code
$ git add scripts/menu-ssh/menu-ssh
$ git commit -m "feat: add interactive ssh menu manager"
$ git push origin main

Usar menu-ssh em Scripts

Você pode chamar menu-ssh de outros scripts:

1
2
3
4
5
#!/bin/bash
# Seu script

# Conectar a um host via menu-ssh
menu-ssh

Criar Alias para menu-ssh

Se preferir um nome mais curto:

1
2
3
4
5
# Adicione ao ~/.bash_aliases
alias sshmenu='menu-ssh'

# Agora você pode usar
$ sshmenu

Conclusão

O menu-ssh é uma ferramenta poderosa que melhora significativamente a experiência de gerenciamento de hosts SSH. Ele oferece:

✓ Interface intuitiva com fzf ✓ Integração perfeita com estrutura modular ✓ Validação completa de entrada ✓ Backups automáticos ✓ Suporte a ProxyJump e múltiplas chaves ✓ Categorização automática de hosts ✓ Feedback claro e profissional

O Que Você Alcançou

✓ Instalação do menu-ssh ✓ Criação de link simbólico para acesso global ✓ Compreensão de como o script funciona ✓ Capacidade de adicionar, remover e listar hosts ✓ Testes de conexão e conexão via interface visual ✓ Integração com estrutura workspace-as-code

Próximos Passos Imediatos

  1. Teste o menu-ssh:
    1
    2
    
    $ menu-ssh
    # Selecione "Listar hosts"
    
  2. Adicione seus primeiros hosts:
    1
    2
    
    $ menu-ssh
    # Selecione "Adicionar novo host"
    
  3. Teste conexões:
    1
    2
    
    $ menu-ssh
    # Selecione "Testar conexão"
    
  4. Commit suas mudanças:
    1
    2
    3
    4
    
    $ cd ~/workspace-as-code
    $ git add scripts/menu-ssh/
    $ git commit -m "feat: add interactive ssh menu manager"
    $ git push origin main
    

Próximo Tutorial

Com a automação e interface visual para SSH estabelecidas, o próximo passo é explorar a customização da interface gráfica, tornando nosso ambiente de trabalho visualmente coeso e agradável.


Recursos Adicionais


Fim da Parte 4.5

Próxima: Customizando o GNOME Terminal e a Interface do Ubuntu

This post is licensed under CC BY 4.0 by the author.